写点什么

“工厂、构造、原型” 设计模式与 JS 继承

作者:掘金安东尼
  • 2022 年 7 月 28 日
  • 本文字数:1779 字

    阅读完需:约 6 分钟

“工厂、构造、原型” 设计模式与 JS 继承

我们可以明显的感受到:JS 要实现面向对象(继承的能力),离不开这 3 种设计模式;


原型链 + 构造函数 = 组合继承


本篇带来一个新的继承方式:寄生继承,它由工厂模式和构造函数模式组成,即


工厂+构造函数 = 寄生继承

正文

正是由于:原型链继承和构造函数继承的 “毛病”


  1. 原型链继承:所有继承的属性和方法都会在对象实例间共享,无法做到实例私有。

  2. 构造函数继承:子类不能访问父类原型上的方法。


组合继承应运而生:


function SuperType(name){     this.name = name;     this.colors = ["red", "blue", "green"];}function SubType(name, age){     SuperType.call(this, name) // 构造函数继承 (两次调用父类构造函数)     this.age = age;}
SuperType.prototype.sayName = function() { console.log(this.name);}
SubType.prototype = new SuperType() // 原型链继承 (一次调用父类构造函数)SubType.prototype.sayAge = function() { console.log(this.age);}
let s1 = new SubType("Nicholas", 29)let s2= new SubType("Greg", 27)
s1.colors.push("yellow")console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']console.log(s2.colors) // ['red', 'blue', 'green']
s1.sayName() // Nicholass2.sayName() // Greg
s1.sayAge() // 29s2.sayAge() // 27
复制代码


但是呢?这样做,会有效率问题,父类构造函数始终会被调用两次:一次是在子类构造函数中调用,另一次在是创建子类原型时调用。


本质上,子类原型最终是要包含超类对象的所有实例属性,子类构造函数只要在执行时重写自己的原型就行了。


这个时候有一个新的思路!


不通过调用父类构造函数给子类原型赋值,而是取得父类原型的一个副本。使用寄生式继承来继承父 类原型,然后将返回的新对象赋值给子类原型。


核心代码是:通过工厂的方式,增强一个新对象:


function createAnother(original){     let clone = Object(original); // 通过调用函数创建一个新对象     clone.sayHi = function() { // 以某种方式增强这个对象         console.log("hi");     };     return clone; // 返回这个对象}
复制代码


将组合代码改造一下,完整代码是:


function inheritPrototype(subType, superType) {     let prototype = Object(superType.prototype); // 创建对象     prototype.constructor = subType; // 增强对象     subType.prototype = prototype; // 赋值对象}
function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"];}function SubType(name, age) { SuperType.call(this, name); // 构造函数继承(只调了一次) this.age = age;}
SuperType.prototype.sayName = function() { console.log(this.name);};
inheritPrototype(SubType, SuperType); // 寄生继承SubType.prototype.sayAge = function() { console.log(this.age);};
let s1 = new SubType("Nicholas", 29)let s2= new SubType("Greg", 27)
s1.colors.push("yellow")console.log(s1.colors) // ['red', 'blue', 'green', 'yellow']console.log(s2.colors) // ['red', 'blue', 'green']
s1.sayName() // Nicholass2.sayName() // Greg
s1.sayAge() // 29s2.sayAge() // 27
复制代码


这里只调用了一次 SuperType 构造函数,避免了 SubType.prototype 上不必要也用不到的属性;而且,原型链仍然保持不变,instanceof 操作符和 isPrototypeOf() 方法正常有效。


寄生式组合继承可以算是【引用类型】继承的最佳模式


os:不过这里的增强写法,理解起来真是怪,为什么父类的显示原型的构造函数等于子类?


SuperType.prototype.constructor=== SubType // true
复制代码


大概是为了,通过寄生实现:父类、子类都由同一函数构造;


SubType === SubType.prototype.constructor // true
SuperType.prototype.constructor === SubType.prototype.constructor // true
复制代码

小结

只要是写 JS 的继承,一定离不开:工厂、构造、原型设计模式;


原型链 + 构造函数 = 组合继承


工厂+构造函数 = 寄生继承;


组合继承和寄生继承是最常用的两种继承方式。


......


u1s1,class 出来前,写 JS 实现继承,是真滴麻烦 QAQ


我是掘金安东尼 🤠 100 万阅读量人气前端技术博主 💥 INFP 写作人格坚持 1000 日更文 ✍ 关注我,陪你一起度过漫长编程岁月 🌏

发布于: 10 小时前阅读数: 12
用户头像

还未添加个人签名 2022.07.14 加入

社会我瓜哥,人狠话不多😎 微信 anthony1453,加我交个朋友😎 正联合【机械工业出版社】出版《程序员成长手册》,敬请期待😎 真正的大师,永远怀着一颗学徒的心(易)😎

评论

发布
暂无评论
“工厂、构造、原型” 设计模式与 JS 继承_JavaScript_掘金安东尼_InfoQ写作社区