注:本文内容知识来源 https://www.cnblogs.com/ranyonsue/p/11201730.html
故事一
1. 原型链继承
子类构造函数.prototype = new 父类构造函数
缺点
- 子类实例无法像父类构造函数传参。
- 子类实例共享父类实例的属性,且父类原型上的属性是共用的。
- 只能继承一个父类。
思考
子类实例:我能通过隐式原型__proto__
拿到父类实例的属性,但是我俩是共用相同的属性哎!虽然他是我爸,但是…而且,别人java都允许有干爹(接口),为啥我没有?
我:得加钱…
于是,构造函数继承出现了。
2. 构造函数继承
function 子类构造函数() {
// 设置子类属性
this.属性 = ...
// 执行父类构造函数
父类构造函数1.call()
父类构造函数2.call()
// ...
}
优缺点
缺点:
- 无法继承父类原型的属性,只继承了父类构造函数属性。
- 臃肿,每个实例都拥有父类构造函数的副本。
优点:可以继承多个父类。
思考
子类实例:通过一系列的call、apply,我终于有干爹了!但是,我亲爸还是我亲爸啊,为什么我亲爸的东西(无法通过原型链__proto__
拿到父类原型上的数据)我不能用了呢?不行,我得跟我爸说去!
我:还得加钱…
于是,组合式继承出现了。
3. 组合式继承
function 子类构造函数() {
// 添加子类属性
this.属性 =
// 执行父类构造函数
父类构造函数1.call()
父类构造函数2.call()
// ...
}
子类构造函数.prototype = new 父类构造函数
优缺点
缺点:重复调用了两次父类的构造函数
优点:
- 允许继承多个类。
- 可以拿到父类原型的数据。
思考
子类实例:完美!我不仅有很多干爸(call、apply),而且我还能和亲爸共享属性。
路人:看起来不错!但是,作为有洁癖的路人发现父类构造函数执行了有可能执行2次,浪费了内存。
我:不干了,跑路!
于是,故事一告1段落…
故事二
4. 原型式继承
function container(父类实例) {
function 子类构造函数() {}
子类构造函数.prototype = 父类实例
return new 子类构造函数
}
// 使用
const 子类实例 = container(父类实例)
缺点
- 不便复用(新实例的属性只能打点)
- 继承单一
思考
子类实例:这这这,是我吗?我说他不是我(没有明确的子类函数),你说他就是我,我觉得这TM根本就不是我!
我:得加钱…
5. 寄生式继承
function container(父类实例) {
function 子类构造函数() {}
子类构造函数.prototype = 父类实例
return new 子类构造函数()
}
function 真丶子类构造函数(父类实例) {
const 子类实例 = container(父类实例)
// 添加属性
子类实例.属性 = ...
return 子类实例
}
// 使用
const 子类实例1 = 真丶子类构造函数(父类实例)
优缺点
缺点:子类未用到原型,无法实现复用。
优点:有明确的子类函数,不需创建自定义类型。
思考
子类实例:爹啊~我终于找到你了(有明确的子类函数)!我还可以感受到爸爸对我的爱(子类实例可通过原型链访问到父类显式原型prototype
的数据),但是妈妈的爱我感受不到啊!(真丶子类构造函数未用到原型,无法复用相关数据)。
我:我的错我的错,马上改!
6. 寄生式组合继承
function container(父类实例) {
function 子类构造函数() {}
子类构造函数.prototype = 父类实例
return new 子类构造函数()
}
// 组合
function Sub() {
父类构造函数.apply()
}
Sub.prototype = container(父类实例)
// 使用
const 子类实例 = new Sub()
思考
子类实例们:真好,爸爸妈妈的爱都在哎,我们是幸福的一家人!
总结
此时,寄生组合式继承实现功能具体如下:
- 干爹 — call、apply,实现多继承。
- 爸爸妈妈的爱 — 子类实例可以访问到子类和父类构造函数的原型数据。
- 这就是我! — 有明确的子类函数。
- 消除洁癖 — 父类构造函数仅执行一次。