一、面向对象(Object Oriented Programming)
什么是面向对象?
面向对象是一种软件开发方法,它将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的灵活性、重用性和扩展性。
面向对象的三大基本特征:封装、继承、多态
封装:将同一类的属性和方法放在一个类(构造函数中),当需要使用该类中的属性或方法时,通过该类来创建对象,并使用该对象去调用属性或方法。对象是封装的最基本单位。
继承:子类去调用父类里面的属性和方法,就表示子类继承了父类中的属性和方法。
多态:说白了多态就是相同的事物,一个接口,多种实现,同时在最初的程序设定时,有可能会根据程序需求的不同,而不确定哪个函数实现,通过多态不需要修改源代码,就可以实现一个接口多种解决方案。
多态的表现形式重写与重载
重写:子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承 父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
重载:重载就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函 数或者方法之间,互相称之为重载函数或者方法。在JavaScript中,同一个作用域,出现两个 名字一样的函数,后面的会覆盖前面的,所以 JavaScript 没有真正意义的重载。
类与对象:
类:一组有相同特性和相同行为的对象,具有相同属性和相同方法的对象的抽象就是类。
对象:类的实例是对象,对象把数据及对数据的操作方法放在一起,作为一个相互依存的整体。
对象的抽象是类
类与对象的关系:模板 -> 产品
“JavaScript中所有事物都是对象”,如 字符串、数组、函数…等等。
因为所有事物均继承自Object,都是Object的实例。
对象是带有属性和方法的集合。
变量和属性: 变量是自由的,属性是属于对象的,是与对象相关的值。
函数和方法: 函数是自由的,方法是属于对象的,是与对象相关的函数。
面向对象与面向过程的区别:
面向过程是一种直接的编程方法,它是按照编程的思想考虑问题,通过顺序执行一组语句来实现一个功能
面向过程可以说是从细节方面考虑问题;面向对象可以说是从宏观方面考虑问题
自定义构造函数
使用new创建对象的函数
function fn() {}
var f = new fn()
console.log(f)
new一个构造函数的时候,经历了什么: (1)在函数中创建一个空对象 (2)将函数中的this改成这个对象 (3)如果函数中有代码,就执行函数中的代码 (4)将这个对象返回
注意:
(1)构造函数需要和new关键字连用
(2)如果没有new关键字,直接调用构造函数,那么构造函数则会作为一个普通函数使用
(3)构造函数中,不能书写return关键字;如果使用return关键字返回内容时;返回的如果是基本数据类型的值,则没有意义;如果返回的是复杂数据类型的值,则构造函数没有意义
(4)构造函数的名称,首字母大写
(5)使用new关键字去调用构造函数时,如果没有传递参数则可以省略()不写
(6)构造函数中的this关键字,指向就是当前使用构造函数生成的实例对象
原型:
原型就是一个为对象实例定义了一些公共属性和公共方法的对象模板。
prototype:每一个函数天生自带一个成员,叫做prototype,是一个对象空间;
prototype(原型)属性,这个属性是一个指针,指向一个对象,这个对象的用途是包含特定类型的所有实例共享的属性和方法,即这个原型对象是用来给实例共享属性和方法的。 使用这个原型对象来共享实例的属性和方法的模式就叫原型模式
__proto__:每一个对象都天生自带一个成员,叫做__proto__,指向的是原型空间对象
注意:在自定义构造函数时:
1)属性一般是写在构造函数中
2)方法一般是写在原型空间里面
步骤:
(1)定义空构造函数
(2)调用它传参数 - 效果要产生的范围(最大标签的选择器)
(3)构造函数中 - 添加属性 - 将需要作的标签都放在this上
(4)调用方法实现效果 - 给原型上添加方法实现效果
原型链:
对象之间的继承关系通过构造函数的prototype指向父类对象,直到指向Object对象为止形成的指向链条。
对象有原型,原型是一个对象,只要是对象就会有原型,所以原型也有原型,这样形成的链式结构叫原型链
原型链的作用:
对象访问属性和方法时,先在自己中,自己没有就找原型,原型没有就顺着原型链向上级原型找...直到顶级原型也没有,那访问的如果是属性,则返回undefined,如果是方法,则报错:该方法 is not afunction
赋值原则:
查找当前对象中是否存在该属性或方法;如果存在,则直接覆盖;如果不存在,则该当前对象添加该属性或方法;
注意:赋值规则,只会在当前对象的区域中操作该属性或方法,不会去对象的原型中操作
构造器
每一个原型对象天生带有一个属性叫做constructor,这个属性指的是这个原型对象所对应的构造函数。
constructor:代码构造器,主要表示当前实例对象是由哪个构造函数创建,也可以用来检测复杂数据类型
instanceof:运算符用于测试构造函数的prototype属性是否出现在对象的原型链中的任何位置
深拷贝和浅拷贝
浅拷贝:源数据和目标数据不共享同一个数据空间,如果源数据中包含引用数据类型,源数据中的引用数据和新数据中的引用数据还在共享同一个数据空间。
//数组浅拷贝
var arr = [ 1, false, {name: '张三', age: 12}, function() {} ]
var brr = []
// 遍历
arr.forEach(item => brr.push(item))
// 或
var brr = [...arr]
// 数组方法
var brr = arr.slice()
var brr = arr.concat()
// 比较两个数据
cosnole.log(arr === brr) // false
// 比较其中的引用数据
console.log(arr[2] === brr[2]) // true
//对象浅拷贝
var obj = {
name: '张三',
age: 12,
wife: {
name: '翠花',
age: 13
}
}
var pbj = {}
// 遍历
for(var key in obj) {
pbj[key] = obj[key]
}
// 或
pbj = {...obj}
// 对象方法
//assign方法用于将一个对象中的属性拷贝到另一个目标对象中,返回目标对象。
pbj = Object.assign({}, obj)
// 比较
console.log(obj === pbj) // false
// 比较其中的引用数据
console.log(obj['wife'] === pbj['wife']) // true
深拷贝:深层次的拷贝,新数据和源数据不共享同一个数据空间,其中包含的引用数据也不共享同一个数据空间。、
var arr = [1, false, {name: '张三', age: 12}]
// 利用json方法
var brr = JSON.parse(JSON.stringify(arr))
// 比较
console.log(arr === brr) // false
console.log(arr[2] === brr[2]) // false
json:一种数据格式,类似于数组和对象的数据格式。
我们在项目中存储数据,通常会使用json文件进行存储,一种后缀为.json的文件。
json文件格式要求:
{
"name": "zhangsan",
"age": 12
}
//或
[12, "lisi"]
//这种格式的数据叫做json。
json方法:
JSON.stringify(对象/数组) // 将对象或数组转成json字符串
JSON.parse(json字符串) // 将json字符串转成数组或对象
var obj = {
name: '张三',
age: 12,
wife: {
name: '翠花',
age: 13
},
children: ['大毛', '二毛', '小明'],
eat: function() {
console.log('吃东西')
}
}
var objStr = JSON.stringify(obj)
console.log(objStr)
var arr = [1, false, {name: '张三', age: 12}, function() {console.log('函数')}]
var arrStr = JSON.stringify(arr)
console.log(arrStr)
var pbj = JSON.parse(objStr)
console.log(pbj)
var brr = JSON.parse(arrStr)
console.log(brr)
每次转换后,会得到一个新数据,所以可以利用json方法实现深拷贝。
但json方法的转换,会丢失函数。数组或对象中如果包含有函数,转换后的结果中是没有函数的。
递归实现深拷贝
function deepCopy(data) {
var newData
if(Object.prototype.toString.call(data) === '[object Object]') {
newData = {}
} else if(Object.prototype.toString.call(data) === '[object Array]') {
newData = []
} else {
return data
}
for(var key in data) {
if(typeof data[key] === 'object') {
newData[key] = deepCopy(data[key])
} else {
newData[key] = data[key]
}
}
return newData
}
var obj = {
name: '张三',
age: 12,
wife: {
name: '翠花',
age: 13
},
children: ['大毛', '二毛', '小明'],
eat: function() {
console.log('吃东西')
}
}
var pbj = deepCopy(obj)
console.log(pbj);
console.log(pbj === obj); // false
console.log(pbj['wife'] === obj['wife']); // false
var arr = [1, false, {name: '张三', age: 12}, function() {console.log('函数')}]
var brr = deepCopy(arr)
console.log(brr);
console.log(arr === brr); // false
console.log(arr[2] === brr[2]); // false