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

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

序言



我们在前一篇文章《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() // Nicholas
s2.sayName() // Greg
s1.sayAge() // 29
s2.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() // Nicholas
s2.sayName() // Greg
s1.sayAge() // 29
s2.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


相关文章
|
16天前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
25天前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
19天前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
19天前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
31 1
|
2月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript设计模式--访问者模式
【10月更文挑战第1天】
33 3
|
2月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
2月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
2月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
22 1
|
2月前
|
JavaScript 前端开发
JS的几种继承方式
JavaScript中的几种继承方式视频。
14 0