开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情
实现继承的几种姿势
贴心的JavaScript为我们准备了N多种实现继承的姿势,体验丰富、欲生欲死、欲罢不能!了解前三个就基本可以了。
| 实现方式 | 优缺点 | |
|---|---|---|
| 原型继承 | 手动设置原型链实现继承: 🔸 subObj.__proto__ == parentObj🔸 Object.setPrototypeOf(obj , parentObj)🔸 SubFunc.prototype = parentObj🔸 Object.create(proto, propertiesObject) | 🔴原型__proto__对象是共享的,大家共享原型上的属性值(特别是值为引用)🔴无法向父类传递参数 |
| 借用构造函数 | 调用构造函数,借用其this的属性、方法,本质是复制,没有“继承”关系。parentFunc.call(this) | 🟢避免了属性共享,可以传递参数 🔴方法无法复用,每个实例对象都重新创建方法 |
| 组合继承 | 上面两种的组合: 🔸 借用构造函数:实现属性”继承“ 🔸 原型继承:实现方法继承、复用 | 🟢实现了方法的重用,解决了属性共享 🔴至少调用两次父级构造函数?好像也不是什么大事 |
| 寄生组合 | 组合继承的改进版,添加了用一个空构造函数包装父级原型 | 🟢在组合继承基础上,减少了一次父类构造函数的调用。 🔴子级的原型 prototype被覆盖了 |
| 增强寄生组合 | 寄生组合的改进版,把子类原型中的属性手动加回来 | 🟢解决了上面的问题 |
| class类继承 | extends,属性并没有在class.prototype里 | 🟢属性是私有的,方法是共享的,支持传参 |
- 借用构造函数:
parentFunc.call(this),借鸡生蛋!调用构造函数,借用其this的属性、方法,本质是复制,没有“继承”关系。
function Bird() {
this.type = "sam";
this.hi = function () { console.log("hi") };
}
function Duck() {
Bird.call(this); //强制修改Bird()的this,借鸡下蛋
}
let duck = new Duck();
console.log(duck instanceof Bird); //false 和Bird没继承关系
console.log(duck instanceof Duck); //true
- 组合继承:借用构造函数 + 原型继承 的结合体:借用构造函数实现属性”继承; 原型继承实现方法继承、复用。
function Bird(name) {
this.name = name;
this.colors = ["red"];
}
Bird.prototype.fly = function () { console.log(this.name + " fly!") };
Bird.prototype.type = "鸟类"; //需要共享的属性
function Duck(name) {
Bird.call(this, name); //借用构造函数:实现属性”继承“。调用一次Bird构造函数
this.price = 100;
}
Duck.prototype = new Bird(); //原型继承:实现方法继承、复用。调用一次Bird构造函数
//修正constructor,不修正也没啥,就是别人用new duck.constructor("gaga")创建对象时不对
Duck.prototype.constructor = Duck;
let duck = new Duck("sam");
console.log(duck instanceof Bird); //true
console.log(duck instanceof Duck); //true
console.log(duck.fly == (new Duck()).fly); //true
duck.colors.push("green");
console.log(duck.colors, new Duck("ww").colors); // ['red', 'green'] ['red'] //没有共享
- 寄生组合式继承:基本思路同组合继承,算是组合继承的改进版,直接设置子级的原型
F.prototype,减少一次父级构造函数的调用。
function inherit(parentFunc, childFunc) {
let SuperF = function () { }; //用一个空构造函数封装父级
SuperF.prototype = parentFunc.prototype;
childFunc.prototype = new SuperF(); //new 这个空构造函数,不用调用父级构造函数了。
childFunc.constructor = childFunc;
}
//更粗暴的做法
function inherit2(parentFunc, childFunc) {
childFunc.prototype = parentFunc.prototype; //修改prototype
childFunc.constructor = childFunc;
}
//父级
function Bird(name) {
this.name = name;
this.colors = ["red"];
}
Bird.prototype.fly = function () { console.log(this.name + " fly!") };
//子类
function Duck(name) {
Bird.call(this, name);
this.price = 100;
}
Duck.prototype.cry = function () { console.log(this.name + " cry!") }; //Duck.prototype原有的属性被后面覆盖了
Duck.prototype = Bird.prototype; //修改prototype
Duck.constructor = Duck;
// inherit2(Bird, Duck); //同上
let duck = new Duck("sam");
console.log(duck instanceof Bird); //true
console.log(duck instanceof Duck); //true
console.log(duck.fly == (new Duck()).fly); //true
duck.colors.push("green");
console.log(duck.colors, new Duck("ww").colors); // ['red', 'green'] ['red'] //没有共享
- 增强寄生组合:寄生组合的继续改进版,把子类原型中的属性手动加回来。
function inherit(parentFunc, childFunc) {
let proto = parentFunc.prototype;
//把子类原型的所有属性复制到一起
Object.keys(childFunc.prototype).forEach(key =>
Object.defineProperty(proto, key, { value: childFunc.prototype[key] }))
childFunc.prototype = proto;
childFunc.constructor = childFunc;
}
//父级
function Bird(name) {
this.name = name;
this.colors = ["red"];
}
Bird.prototype.fly = function () { console.log(this.name + " fly!") };
//子类
function Duck(name) {
Bird.call(this, name);
this.price = 100;
}
Duck.prototype.cry = function () { console.log(this.name + " cry!") };
inherit(Bird, Duck);
let duck = new Duck("sam");
duck.cry(); //sam cry! //没有丢失
console.log(duck instanceof Bird); //true
console.log(duck instanceof Duck); //true
console.log(duck.fly == (new Duck()).fly); //true
duck.colors.push("green");
console.log(duck.colors, new Duck("ww").colors); // ['red', 'green'] ['red'] //没有共享
©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀