继承
废话不多说,直接上干货
一共有6种方式:
1. 原型链继承
2. 经典继承
3. 组合继承
4. 原型式继承
5. 寄生式继承
6. 寄生组合式继承
1. 原型链继承
子类原型作为父类的实例对象
-
优点:
- 继承到父类[挂载在实例上的方法:this.name]
- 继承到父类原型上的属性和方法
-
缺点:
-
引用类型的属性会在实例之间共享。 [不管生成多少个实例对象,都只是调用了一次父类,访问引用属性时去到的是同一个堆地址]
-
子类不能向父类传递参数
-
-
代码实现
function Father() { } father.prototype.getName = function () { return this.name } function Child(params) { } // 原型链继承 Child.prototype = new Father()
2. 经典继承
在子类中调用父类,让子类的this拿到父类中的属性和方法
- 优点:
- 引用类型的属性不被共享。[每生成一个子类实例,都会重新调用一次父类,堆会重新开辟一个地址存储引用属性]
- 子类可以向父类传递参数
- 缺点:
- 不能继承父类原型上的方法。
- 父类中的方法,每生成一次子类实例,方法都会再创建一遍。
- 代码实现
function Father(age) {
}
function Child() {
// 经典继承
Father.call(this,age)
}
let c = new Child()
3. 组合继承:
是原型链继承和经典继承的结合版
-
优点:有着原型链继承和经典继承的优点。
-
缺点:在创建第一个子类实例时,会调用两次父类构造函数
-
代码实现
function Father(params) { } function Child(params) { Father.call(this) //经典继承 } Child.prototype = new Father() //原型链继承 let c = new Child()
4. 原型式继承(对象)
借助原型链,是Object.crate()的模拟实现 步骤:(函数接收一个对象作为参数) 创建一个临时函数 让函数原型变为 参数对象 返回函数实例对象
-
优点:让子对象继承到了父对象中的属性
-
缺点:引用类型属性在子对象中共享
-
代码实现
function cloneObj(obj) { function fn(params) { //创建临时函数 } fn.prototype = obj // 将临时函数的原型变为 obj return new fn() // 返回函数的实例对象 }
5. 寄生式继承(对象)
比原型式的基础上多了一步增强对象 步骤:(接收子对象和父对象作为参数) 创建一个临时变量,初始化为原型式继承父类原型的对象 增强对象[在这个对象上增加属性] 返回这个对象
- 优点:子对象继承父对象的属性,并且可以得到一些额外属性[函数中的增强对象操作]
- 缺点:引用类型属性在实例之间共享
- 代码实现
function cloneObj(obj) {
let clone = Object.create(obj) //复制对象
clone.setName = function () { //增强对象
console.log('hello');
}
return clone
}
6. 寄生组合式继承
将组合式和寄生式结合。对组合式继承进行优化,让子类原型和父类间接的联系起来. 减少父类构造函数的调用.
- 优点:
- 减少了父类构造函数的调用次数
- 父类中引用属性在实例间不被共享
- 子类可以向父类传递参数
- 代码实现
function prototype(father,son) {
// clone是中间人
let clone = Object.create(father.prototype) // 让clone对象拿到父类原型。为了让后续的子类和父类产生联系
clone.constructor = son // 将constructor属性指向son【防止constructor丢失】
son.prototype = clone // 让子类原型变成 clone对象
}