1.原型链

function SuperType(){
this.property = true;
}

SuperType.prototype.getSuperValue = function(){
return this.property;
};
function SubType(){
this.subproperty = false;
}
//继承了SuperType
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function (){
return this.subproperty;
};
var instance = new SubType();
alert(instance.getSuperValue()); //true

实现的本质是重写原型对象,代之以一个新类型的实例。

2.借用构造函数

function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){
//继承了SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"

如果仅仅是借用构造函数,那么也将无法避免构造函数模式存在的问题——方法都在构造函数中定
义,因此函数复用就无从谈起了。而且,在超类型的原型中定义的方法,对子类型而言也是不可见的,结
果所有类型都只能使用构造函数模式。考虑到这些问题,借用构造函数的技术也是很少单独使用的。

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;
}
//继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27

在这个例子中,SuperType 构造函数定义了两个属性:name 和colors。SuperType 的原型定义
了一个方法sayName()。SubType 构造函数在调用SuperType 构造函数时传入了name 参数,紧接着
又定义了它自己的属性age。然后,将SuperType 的实例赋值给SubType 的原型,然后又在该新原型
上定义了方法sayAge()。这样一来,就可以让两个不同的SubType 实例既分别拥有自己属性——包
括colors 属性,又可以使用相同的方法了。
组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JavaScript 中最常用的继
承模式。而且,instanceof 和isPrototypeOf()也能够用于识别基于组合继承创建的对象。

4.原型式继承

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

在object()函数内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的
原型,最后返回了这个临时类型的一个新实例。从本质上讲,object()对传入其中的对象执行了一次

浅复制。来看下面的例子。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

克罗克福德主张的这种原型式继承,要求你必须有一个对象可以作为另一个对象的基础。如果有这么
一个对象的话,可以把它传递给object()函数,然后再根据具体需求对得到的对象加以修改即可。在这
个例子中,可以作为另一个对象基础的是person 对象,于是我们把它传入到object()函数中,然后该
函数就会返回一个新对象。这个新对象将person 作为原型,所以它的原型中就包含一个基本类型值属性
和一个引用类型值属性。这意味着person.friends 不仅属于person 所有,而且也会被anotherPerson
以及yetAnotherPerson 共享。实际上,这就相当于又创建了person 对象的两个副本。
ECMAScript 5 通过新增Object.create()方法规范化了原型式继承。这个方法接收两个参数:一
个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下,
Object.create()与object()方法的行为相同。
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

Object.create()方法的第二个参数与Object.defineProperties()方法的第二个参数格式相
同:每个属性都是通过自己的描述符定义的。以这种方式指定的任何属性都会覆盖原型对象上的同名属
性。例如:
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = Object.create(person, {
name: {
value: "Greg"
}
});
alert(anotherPerson.name); //"Greg"

支持Object.create()方法的浏览器有IE9+、Firefox 4+、Safari 5+、Opera 12+和Chrome。
在没有必要兴师动众地创建构造函数,而只想让一个对象与另一个对象保持类似的情况下,原型式
继承是完全可以胜任的。不过别忘了,包含引用类型值的属性始终都会共享相应的值,就像使用原型模
式一样。

5.寄生式继承

寄生式(parasitic)继承是与原型式继承紧密相关的一种思路,并且同样也是由克罗克福德推而广
之的。寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该
函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。以下代码示范了寄
生式继承模式。
function createAnother(original){
var clone = object(original); //通过调用函数创建一个新对象
clone.sayHi = function(){ //以某种方式来增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
在这个例子中,createAnother()函数接收了一个参数,也就是将要作为新对象基础的对象。然
后,把这个对象(original)传递给object()函数,将返回的结果赋值给clone。再为clone 对象
添加一个新方法sayHi(),最后返回clone 对象。可以像下面这样来使用createAnother()函数:
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi"
这个例子中的代码基于person 返回了一个新对象——anotherPerson。新对象不仅具有person
的所有属性和方法,而且还有自己的sayHi()方法。
在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示
范继承模式时使用的object()函数不是必需的;任何能够返回新对象的函数都适用于此模式。

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

6.寄生组合式继承

前面说过,组合继承是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/。

JS继承六大模式的更多相关文章

  1. js中State模式的解析及运用

     状态模式,在大的范畴中的定义为当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类.每种编程语言有不同的实现方式,运用的范围也多用于游戏之中. 这里我用javascript来模拟状 ...

  2. js继承之借用构造函数继承

    我的上一篇文章介绍了,原型链继承模式.但是单纯的原型链模式并不能很好地实现继承. 一.原型链的缺点 1.1 单纯的原型链继承最大的一个缺点,来自于原型中包含引用类型的值. 本来,我们没有通过原型链实现 ...

  3. js继承的常用方法

    写在前面的话:这篇博客不适合对面向对象一无所知的人,如果你连_proto_.prototype...都不是很了解的话,建议还是先去了解一下JavaScript面向对象的基础知识,毕竟胖子不是一口吃成的 ...

  4. JS继承方式详解

    js继承的概念 js里常用的如下两种继承方式: 原型链继承(对象间的继承) 类式继承(构造函数间的继承) 由于js不像java那样是真正面向对象的语言,js是基于对象的,它没有类的概念.所以,要想实现 ...

  5. js继承的关系(一)

    js继承的关系多,而且拥有不同的特点.同时也是必须了解掌握的知识点.首先,要先知道什么是构造函数? 构造函数 构造函数和普通函数的区别:仅在于调用方式不同,任何函数,只要通过 new 操作符来调用,那 ...

  6. JS继承的原理、方式和应用

    概要: 一.继承的原理 二.继承的几种方式 三.继承的应用场景 什么是继承? 继承:子类可以使用父类的所有功能,并且对这些功能进行扩展.继承的过程,就是从一般到特殊的过程.要了解JS继承必须首先要了解 ...

  7. 简单易懂的JS继承图解

    JS继承的实现方式一共有八种.下面我们来一个一个瞅一下.注意️:文章依据个人理解,难免有问题,还望各位及时指出!!!!! 原型链继承 借用构造函数继承 组合继承 原型式继承 寄生继承 寄生组合式继承 ...

  8. Js 职责链模式 简单理解

    js 职责链模式 的简单理解.大叔的代码太高深了,不好理解. function Handler(s) { this.successor = s || null; this.handle = funct ...

  9. js继承

    js继承有5种实现方式: 继承第一种方式:对象冒充 function Parent(username){ this.username = username; this.hello = function ...

随机推荐

  1. 文成小盆友python-num6 -反射 ,常用模块

    本次主要内容: 内容补充 python中的反射 常用模块 一,内容补充: 利用上次说到的递归的方法来实现阶乘. 说明:利用函数递归的方法来实现阶乘如: 1*2*3*4*5*6*7 代码实现如下: de ...

  2. hdu 2016

    Problem Description 输入n(n<100)个数,找出其中最小的数,将它与最前面的数交换后输出这些数.   Input 输入数据有多组,每组占一行,每行的开始是一个整数n,表示这 ...

  3. Visual Studio express

    之前一直没用过Visual Studio的express版本.在最近一段时间,使用VS2010和2012的破解版都有点问题.vs2010突然不能使用,需要重新安装,家里和单位两台电脑都如此.家里一台电 ...

  4. Hibernate学习笔记--第一个Hibernate框架程序

    一般使用集成开发环境是,把所需的类库添加到项目属性的库路径中,开发工具在部署时会自动复制所需要的类包到WEB-INF\lib目录下 MyEclipse中: 创建项目,右击项目->myeclips ...

  5. matlab读取多幅图片,并对读取的图片降采样和双三次插值

    clear all clc im = {}; %%创建字典im以保存读取的图片 dis = dir('C:\Users\KCl\Documents\MATLAB\SRCNN\Set5\*.bmp'); ...

  6. 怎么通过网站优化来增强SEO效果?

    真正好的网站优化不应该针对哪一个搜索引擎,而应该关注用户的需求.搜索引擎把这这用户指标权重提升,实际上也是希望把更多的精力集中在用户体验的提升上面.那么,这些用户指标应该怎样提升呢?方法多种多样,下面 ...

  7. IDA pro 的Python环境变量设置

    推荐使用IDA PRO6.1+Python2.6 安装完毕Python2.6后,添加如下的环境变量: PYTHONHOME=C:\Python26PATH=%PATH%;C:\Python26LIB= ...

  8. zoj2314 经典 无源汇有上下界最大流 并输出可行流

    ZOJ Problem Set - 2314 Reactor Cooling Time Limit: 5 Seconds      Memory Limit: 32768 KB      Specia ...

  9. IDEA 快捷键整理

    1. IDEA内存优化  \IntelliJ IDEA 9\bin\idea.exe.vmoptions ----------------------------------------- -Xms6 ...

  10. 【转】Android将Activity打成jar包供第三方调用(解决资源文件不能打包的问题)

    Android中引入第三方Jar包的方法(java.lang.NoClassDefFoundError解决办法) 鼠标右键项目,然后属性,然后java buildpath 然后order and ex ...