JavaScript高级程序设计之面向对象的程序设计之继承之寄生组合式继承第6.3.6讲笔记

来源:互联网 发布:vs2017写c语言 编辑:程序博客网 时间:2024/04/30 17:01
前面说过,组合继承是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;}SubType.prototype = new SuperType(); //第一次调用SuperType()SubType.prototype.constructor = SubType;SubType.prototype.sayAge = function(){alert(this.age);};

加粗字体的行中是调用SuperType 构造函数的代码。在第一次调用SuperType 构造函数时,
SubType.prototype 会得到两个属性:name 和colors;它们都是SuperType 的实例属性,只不过
现在位于SubType 的原型中。当调用SubType 构造函数时,又会调用一次SuperType 构造函数,这
一次又在新对象上创建了实例属性name 和colors。于是,这两个属性就屏蔽了原型中的两个同名属
性。图6-6 展示了上述过程。

如图 6-6 所示,有两组name 和colors 属性:一组在实例上,一组在SubType 原型中。这就是调
用两次SuperType 构造函数的结果。好在我们已经找到了解决这个问题方法——寄生组合式继承。
所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背
后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型
原型的一个副本而已。本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型
的原型。寄生组合式继承的基本模式如下所示。
function inheritPrototype(subType, superType){var prototype = object(superType.prototype); //创建对象prototype.constructor = subType; //增强对象subType.prototype = prototype; //指定对象}

这个示例中的inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两
个参数:子类型构造函数和超类型构造函数。在函数内部,第一步是创建超类型原型的一个副本。第二
步是为创建的副本添加constructor 属性,从而弥补因重写原型而失去的默认的constructor 属性。
最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我们就可以用调用inherit-
Prototype()函数的语句,去替换前面例子中为子类型原型赋值的语句了,例如:

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()。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。

YUI 的YAHOO.lang.extend()方法采用了寄生组合继承,从而让这种模式首次
出现在了一个应用非常广泛的JavaScript 库中。要了解有关YUI 的更多信息,请访问
http://developer. yahoo.com/yui/。    

6.4 小结
ECMAScript 支持面向对象(OO)编程,但不使用类或者接口。对象可以在代码执行过程中创建和
增强,因此具有动态性而非严格定义的实体。在没有类的情况下,可以采用下列模式创建对象。
 工厂模式,使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这个模式后来
被构造函数模式所取代。
 构造函数模式,可以创建自定义引用类型,可以像创建内置对象实例一样使用new 操作符。不
过,构造函数模式也有缺点,即它的每个成员都无法得到复用,包括函数。由于函数可以不局
限于任何对象(即与对象具有松散耦合的特点),因此没有理由不在多个对象间共享函数。
 原型模式,使用构造函数的prototype 属性来指定那些应该共享的属性和方法。组合使用构造
函数模式和原型模式时,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。
JavaScript 主要通过原型链实现继承。原型链的构建是通过将一个类型的实例赋值给另一个构造函
数的原型实现的。这样,子类型就能够访问超类型的所有属性和方法,这一点与基于类的继承很相似。
原型链的问题是对象实例共享所有继承的属性和方法,因此不适宜单独使用。解决这个问题的技术是借
用构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的
属性,同时还能保证只使用构造函数模式来定义类型。使用最多的继承模式是组合继承,这种模式使用
原型链继承共享的属性和方法,而通过借用构造函数继承实例属性。
此外,还存在下列可供选择的继承模式。
 原型式继承,可以在不必预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅
复制。而复制得到的副本还可以得到进一步改造。
 寄生式继承,与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强
对象,最后返回对象。为了解决组合继承模式由于多次调用超类型构造函数而导致的低效率问
题,可以将这个模式与组合继承一起使用。
 寄生组合式继承,集寄生式继承和组合继承的优点与一身,是实现基于类型继承的最有效方式。

0 0