继承的概念:通过一定的方式实现让某个类型A获取另外一个类型B的属性和方法。其中类型A称为子类型,类型B称为父类型或超类型。
JavaScript中的继承:Object
是所有对象的父类型,所有Javascript中的对象都是直接或间接继承自Object
。
继承有两种方式:接口继承和实现继承,在JS中只支持实现继承,实现继承主要依赖原型链来完成。
说明:其他语言中继承通常通过类来实现,JS中没有类的概念(ES6新增了class)。JS中的继承是某个对象继承继承另外一个对象,是基于对象的。
1. 属性拷贝(混入式继承)
属性拷贝是浅拷贝,复制引用类型时,只会复制地址,两者会共享同一数据,修改一个会影响另外一个。
在ES6中添加了一个Object.assign()
属性
|
|
同样会影响父对象中的引用类型属性。
2. 原型式继承
利用构造函数创建出来的对象可以使用原型对象中的属性和方法
简单的原型式继承:
复杂一点的原型式继承:
问题: 1.原型式继承创建出来的子对象的构造器属性默认指向父构造函数,需要修正consturctor的指向。
2.无法获取到父对象构造函数中的属性。
扩展内置对象
系统内置的对象(Array
、Object
等)都可以通过在原型对象上添加属性的方式来让所有子对象拥有这一属性。
不建议使用这种方式,在实际开发中通常是多人合作。如果都扩展内置对象,后期将难以维护,同时也会引发覆盖等安全问题。
安全的扩展内置对象
- 提供一个自定义构造函数
- 设置构造函数的原型对象是内置对象的实例
|
|
3. 原型链继承
实现原型链继承的过程:先提供一个子构造函数和父构造函数,设置子构造函数的原型对象是父构造函数的实例。
原型链继承的注意点
在完成原型链继承之后,再进行:
- 修正构造器属性的指向
- 设置子构造函数的原型对象的属性和方法,并且不要使用字面量的方式修改子构造函数属性,会产生替换,应使用对象的动态特性设置
原型链继承的问题
- 无法传递参数给父构造函数
- 用子构造函数创建多个实例对象时,只会复制引用类型数据地址,会产生共享问题。123456789101112131415161718192021//设置父构造函数function Person(name) {this.name = name;this.friends = ['小明'];}//创建子构造函数function Student(num) {this.num = num;}//设置原型链继承Student.prototype = new Person();//修正构造器指向Student.prototype.constructor = Student;//创建实例对象,无法传递参数到父构造函数var stu1 = new Student('2017');var stu2 = new Student('2018');//设置stu1,也会影响stu2stu1.friends.push = '小红';console.log(stu1); //2017 [小明, 小红]console.log(stu2); //2018 [小明, 小红]
Object.creat()方法实现继承
作用:创建一个对象,并设置该对象的原型对象为指定对象
兼容性:ES5
4. 借用构造函数(经典继承 | 伪对象继承)
call
和apply
的用法
用法:Function.prototype.call/apply()
作用:借用其他对象的方法
参数输入:第一个参数为需要借参数的对象,之后的为输入的参数
不同点:call
(对象, 参数1, 参数2…) 参数列表apply
(对象, [参数1, 参数2…]) 参数数组
关于this的指向:使用了call
或apply
方法后,调用的this
指向被绑定的对象(第一个参数)。
借用构造函数的用法
借用构造函数实现继承解决了无法传递参数给父构造函数的问题
5. 组合继承
- 借用构造函数继承获取实例属性和方法
- 原型式继承获取原型属性和方法
组合继承 = 借用构造函数继承 + 原型式继承
问题:子构造函数原型对象与父构造函数原型对象共享同一数据。
|
|
6. 深拷贝继承
浅拷贝(地址拷贝)
|
|
浅拷贝是对值类型属性的复制,遇到引用类型数据时,只能复制其地址
深拷贝(完全拷贝)
- 创建一个深拷贝的函数,提供2个参数,一个是目标对象,一个是需要拷贝的对象
for in
遍历目标对象获取属性,进行判断属性的类型- 如果是值类型,直接赋值
- 如果是引用类型,再调用这个函数,拷贝引用类型里的数据
函数一般是直接调用,不需要修改数据。所以可以当作值类型直接复制地址
在判断对象是否是数组时,我们也可以用到一个方法Array.isArray()
括号内输入需要判断的对象,返回值是Boolean类型。
但是这个方法是ES5才出来,所以具有兼容问题。
利用深拷贝实现继承
- 借用构造函数获取实例属性,
call
和apply
- 深拷贝获取原型属性
完美的拷贝,独立并且互不影响。