JavaScript入门-原型继承方式总结

218 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

实现继承的几种姿势

贴心的JavaScript为我们准备了N多种实现继承的姿势,体验丰富、欲生欲死、欲罢不能!了解前三个就基本可以了。

image


实现方式优缺点
原型继承手动设置原型链实现继承:
🔸 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'] //没有共享

©️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀