JavaScript 继承(五)寄生式、组合寄生继承

寄生式(parasitic)继承是与原型式继承紧密相关的一种思路。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

function object(o) {
    function F(){}
    F.prototype = o;
    return new F();
}

function createAnother(original) {
    // 通过调用函数创建一个新对象
    var clone = object(original);  
    // 以某种方式来增强这个对象
    clone.sayHi = function() {
        alert("hi");
    }

    return clone;
}

上例中,createAnother() 函数接收了一个参数,作为新对象继承的对象。然后,把这个对象(original)传递给 object() 函数,将返回的结果赋值给 clone。再为 clone 对象添加一个新方法 sayHi(),最后返回 clone 对象。

var person = {
    name: "Bert",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = createAnother(person);
anotherPerson.sayHi(); // Hi

上例中, person返回了一个新对象 ---- anotherPerson。新对象不仅具有 person 的所有属性和方法,而且还有自己的 sayHi() 方法。

在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。例子中使用的 object() 函数不是必须的,可以使用任何能够返回新对象的函数都适用于此模式。

使用寄生式继承来为对象添加函数,会由于不能做到函数复用而降低效率;这一点和构造函数模式类似。

寄生组合式继承

组合式继承是 JavaScript 最常用的继承模式;不过它也有自己的不足。组合式继承最大的问题就是无论什么情况下,都会调用两次超类型构造函数;一次是在创建子类型原型的时候,另一次是在子类型构造函数内部。

子类型最终会包含超类型对象的全部实例属性,但我们不得不在调用子类型构造函数时重写这些属性。

function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
    alert(this.name);
}

function SubType(name, age) {
    SuperType.call(this, name); // 第二次调用 SuperType()
    this.age = age;
}

SuperType.prototype = new SuperType(); // 第一次调用 SuperType()
SubType.prototype.sayAge = function() {
    alert(this.age);
}

上例中,有两组 name 和 colors 属性:一组在实例上,一组在 SubType 原型中。这就是调用两次 SuperType 构造函数的结果,通过寄生组合式继承可以解决该问题。

寄生组合式继承:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。(不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一个副本)本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。

function inheritPrototype(subType, superType) {
    var prototype = object(superType.prototype); // 创建对象
    prototype.constructor = subType; // 增强对象
    subType.prototype = prototype; // 指定对象
}

上例中的 inheritPrototype() 函数实现了寄生组合式继承的最简单形式。

  1. 接收两个参数:子类型构造函数和超类型构造函数。
  2. 在函数内部,第一步是创建超类型原型的一个副本。第二部是为创建的副本添加 constructor 属性,从而弥补因重写原型而失去的默认的 constructor 属性。
  3. 将新创建的对象(副本)赋值给子类型的原型。
function SuperType(name) {
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function() {
    alert(this.name);
}

function SubType(name, age) {
    SuperType.call(this, name);

    this.age = age;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function( {
    alert(this.age);
})

这个例子的高效体现在它只调用了一次 SuperType 构造函数,并且因此避免了在 SubType.prototype 上面创建不需要的、多余的属性。与此同时,原型链还能保持不变;能够正常使用 instanceof 和 isPrototypeOf()。

备注:对寄生组合式继承的看法是,通过代码模拟原型链的结构

每个原型链无非就是 proto 属性指向的原型连城的链,既然我们能够通过 prototype 指定对象的原型链,那么同样的指定对象的构造器,就相当于完美实现一个原型继承了。

而之前所涉及到的: 1、借用构造函数继承,实际上就是通过指定对象的构造函数,初始化一些需要的属性;2、组合式继承就是使用 prototype 加上 构造函数;3、寄生式继承通过函数内部做处理继承所需要的函数,在内部添加自定义属性,达到继承效果。

这些都只是在变相的改变对象自身的 prototype,所以只要明白 prototype 指向什么,代表什么,那么玩转 js 的继承就不在话下了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 博客内容:什么是面向对象为什么要面向对象面向对象编程的特性和原则理解对象属性创建对象继承 什么是面向对象 面向对象...
    _Dot912阅读 5,278评论 3 12
  • 1、构造函数模式 [url=]file:///C:/Users/i037145/AppData/Local/Tem...
    横冲直撞666阅读 4,300评论 0 0
  • 1.继承(接口继承和实现继承) 继承是 OO 语言中的一个最为人津津乐道的概念。许多 OO 语言都支持两种继承方式...
    believedream阅读 4,529评论 0 3
  • 本章内容 理解对象属性 理解并创建对象 理解继承 面向对象语言有一个标志,那就是它们都有类的概念,而通过类可以创建...
    闷油瓶小张阅读 4,326评论 0 1
  • JJ的《修炼爱情》在播放器里单曲循环到深夜,这首写给去世的友人的挽歌把我的眼睛带回了多年前,再一次见到那张早已经模...
    辛zora阅读 1,579评论 2 0