几种继承方式
- 原型链
- 盗用构造函数
- 组合继承
- 原型式继承
- 寄生继承
- 寄生式组合继承
原型链
原理:子类的__proto__指向父类的一个实例当作原型对象
缺陷:
1. 原型对象上的引用值会在所有子实例间共享
2. 子类实例化时无法给父构造函数传参
1 | function Father(){} |
盗用构造函数
原理: 在子类中调用父类构造函数
缺陷:无法继承到父类的方法
1 | function Person(name) { |
组合继承
原理: 原型链 + 盗用构造函数
要继承的属性用构造函数
要继承的方法写在原型上
缺陷:调用两次父类构造函数,导致原型对象和子实例上都有父类属性
优势:同时解决了继承引用值属性和方法的继承问题
1 | function Father(name) { |
这是最大缺陷:调用两次父类构造函数,导致子类实例和子类原型都有同名属性—— 寄生式组合继承解决了该问题
原型式继承
原理:利用Object.create
实现继承,无需定义构造函数
缺陷:同原型链继承,原型对象上的引用值会在所有子实例间共享
1 | function object(o) { |
Object.create()
ES5实现的一个方法
Object.create(param1 [, param2])
- param1: 和object方法一样,返回一个对象,该对象原型是param1
- param2: 和defineProperties一样, 可选
寄生式继承
原理:和工厂函数类似,创建一个继承的函数,以某种方式增强对象再返回这个对象
缺陷:同原型式继承,原型对象上的引用值会在所有子实例间共享
1 | function createAnother(original) { |
1 | let person = { |
寄生式组合继承(最优)
最大特点是: 用一个函数实现两个类的继承关系
1 | function inheritPrototype(Son, Father){ |
1 | function Father(name) { |
- 只调用一次父类构造函数,属性不重复
- 引用值不共享
1
2
3
4
5
6
7
8
9
10
11
12// 实例化
let son = new Son('blank',22)
//1. 只调用了一次父类构造函数, 所以子类原型上没有实例属性
console.log(Object.getOwnPropertyNames(son)); //[ 'name', 'colors', 'age' ]
console.log(Object.getOwnPropertyNames(son.__proto__)); //[ 'constructor', 'sayAge' ]
console.log(Object.getOwnPropertyNames(Father.prototype)); //[ 'constructor', 'sayName' ]
//2. 引用值不共享
let son2 = new Son('lzy2', 23)
son.colors.push('b')
son2.colors.push('c')
console.log(son.colors); // [ 'a', 'b' ]
如何理解寄生?
就是把继承这一操作用函数封装起来
ES6 类继承——extends
extends
可以继承任何拥有[[Constructor]]
和原型的对象
- 实例拥有constructor指向构造函数
super
super只能在派生类的构造函数和静态方法中使用
super()
或super.superStaticFun()
调用super() 会调用父类构造函数,并把返回的实例赋值给this
所以必须使用this前调用super()
默认构造函数实例化时,会调用super()并传入所有参数
显式定义构造函数时,要么必须调用super(),要么手动返回一个对象
new.target
得到new 了一个什么东西