最佳的继承范式

寄生组合继承

我们来看一下它的实现方式:

function Object(o){
var TempObject = function(){};
TempObject.prototype = o;
return new TempObject();
} function inheritPrototype(subType,superType){
subType.prototype = Object(superType.prototype);
subType.prototype.constructor = subType;
} function Person(name,age){
this.name = name;
this.age = age;
this.hobby = ["football","swimming"];
} Person.prototype.sayName = function(){
console.log(this.name);
} function Teacher(name,age,subject){
Person.call(this,name,age);
this.subject = subject;
} inheritPrototype(Teacher,Person); var t1 = new Teacher("Tom","32","English");
var t2 = new Teacher("Jane","28","Math"); t1.sayName();
t2.sayName(); t1.hobby.push("cooking");
t2.hobby.push("eat"); console.log(t1.hobby);
console.log(t2.hobby);
console.log(Teacher.prototype.hobby);

运行结果:

Tom
Jane
Array [ "football", "swimming", "cooking" ]
Array [ "football", "swimming", "eat" ]
undefined

这里面结合了寄生式继承(来源于原型式继承)和组合继承,原型式继承可以以一个对象为基础,对基础对象进行浅复制,然后赋给子类。寄生式实现对原型式继承的封装,使之拓展更多的属性和方法。而组合继承解决了原型链对于原型对象全面复制父类构造函数的属性的问题(特别是引用类型,从构造函数的属性变成原型对象的属性,就共享了),同时解决了借用构造函数只能把属性和方法写入构造函数中的问题。组合继承的问题在于原型对象中有多余的属性(因为使用了原型链继承,构造函数的属性都继承在了原型中了,这个问题由原型式继承解决)。

将上述的这些继承方式结合起来,扬长避短,就有了寄生组合继承,解决了上述所描述的问题,所以它是最佳的继承实现方式。

这里简单分析一下:

function Object(o){
var TempObject = function(){};
TempObject.prototype = o;
return new TempObject();
} function inheritPrototype(subType,superType){
subType.prototype = Object(superType.prototype);
subType.prototype.constructor = subType;
}

第一个函数Object(o)传入一个对象,在函数里面创立临时构造函数TempObject,并把对象o赋值给临时构造函数的原型TempObject.prototype,最后返回它的实例,结果是返回的实例中有一个[[prototype]]的指针指向它自身的原型对象,而且这个原型对象里面的属性就是o的属性。为了描述方便,这里我们把这个返回的实例称之为Tab(随便起的名字),用于下面的描述。

在第二个函数中,subType.prototype = Object(superType.prototype);表示把superType的原型通过Object拷贝一份后赋值给Tab的原型对象,而subType的原型subType.prototype就等于这个实例。即subType.prototype有个[[prototype]]指针指向另外一个原型对象(即被继承的原型对象)。当然,此时subType.prototype在继承后也可以有自己方法。这个方法可以写在inheritPrototype() 里面(实现封装),也可以在这个函数外面增加自己的方法。

最后,注意一下运行结果的最后一个结果:undefined,因为通过寄生式继承,在原型对象中,只会继承父类的原型对象,自然在里面就找不到构造函数的属性了。

请看下面的例子(组合继承),这里就存在上述的弊端,即原型对象包含了父类构造函数的属性,它之所以能够不共享这些属性,仅仅只是因为在创建实例的时候创建了同样的属性覆盖了这些共享属性罢了。

// 组合继承
// 父类Person
function Person(name,age){
this.name = name;
this.age = age;
this.hobby = ["football","swimming"];
} Person.prototype = {
constructor:Person,
sayName:function(){
console.log("Hi,"+this.name);
}
} // 子类Teacher
function Teacher(name,age,subject){
Person.call(this,name,age);
this.subject = subject;
} Teacher.prototype = new Person(); var t1 = new Teacher("Tom","32","English");
var t2 = new Teacher("Jane","28","math"); t1.sayName();
console.log(t1.age);
t1.hobby.push("cooking");
console.log(t1.hobby); console.log("----------------------"); t2.sayName();
console.log(t2.age);
t2.hobby.push("eat");
console.log(t2.hobby); console.log(Teacher.prototype.hobby);

运行结果:

Hi,Tom
32
Array [ "football", "swimming", "cooking" ]
----------------------
Hi,Jane
28
Array [ "football", "swimming", "eat" ]
Array [ "football", "swimming" ]

注意到最后一个Array [ "football", "swimming" ],可见组合继承中的原型对象存在多余的属性(来自构造函数)。

参考

  • 《JavaScript高级程序设计》

JavaScript继承的更多相关文章

  1. javascript继承的三种模式

    javascript继承一般有三种模式:组合继承,原型式继承和寄生式继承: 1组合继承:javascript最为广泛的继承方式通过原型链实现对原型属性和方法的继承,通过构造函数实现对实例属性的继承,同 ...

  2. javascript继承机制的设计思想(ryf)

    我一直很难理解Javascript语言的继承机制. 它没有"子类"和"父类"的概念,也没有"类"(class)和"实例" ...

  3. 【读书笔记】javascript 继承

    在JavaScript中继承不像C#那么直接,C#中子类继承父类之后马上获得了父类的属性和方法,但JavaScript需要分步进行. 让Brid 继承 Animal,并扩展自己fly的方法. func ...

  4. 图解JavaScript 继承

    JavaScript作为一个面向对象语言,可以实现继承是必不可少的,但是由于本身并没有类的概念(不知道这样说是否严谨,但在js中一切都类皆是对象模拟)所以在JavaScript中的继承也区别于其他的面 ...

  5. JavaScript强化教程——Cocos2d-JS中JavaScript继承

    javaScript语言本身没有提供类,没有其它语言的类继承机制,它的继承是通过对象的原型实现的,但这不能满足Cocos2d-JS引擎的要求.由于Cocos2d-JS引擎是从Cocos2d-x演变而来 ...

  6. [原创]JavaScript继承详解

    原文链接:http://www.cnblogs.com/sanshi/archive/2009/07/08/1519036.html 面向对象与基于对象 几乎每个开发人员都有面向对象语言(比如C++. ...

  7. javascript继承(六)—实现多继承

    在上一篇javascript继承—prototype最优两种继承(空函数和循环拷贝)(3) ,介绍了js较完美继承的两种实现方案,那么下面来探讨一下js里是否有多继承,如何实现多继承.在这里可以看看j ...

  8. javascript继承(五)—prototype最优两种继承(空函数和循环拷贝)

    一.利用空函数实现继承 参考了文章javascript继承—prototype属性介绍(2) 中叶小钗的评论,对这篇文章中的方案二利用一个空函数进行修改,可以解决创建子类对象时,父类实例化的过程中特权 ...

  9. javascript继承(四)—prototype属性介绍

    js里每一个function都有一个prototype属性,而每一个实例都有constructor属性,并且每一个function的prototype都有一个constructor属性,这个属性会指向 ...

  10. 【JavaScript】重温Javascript继承机制

    上段时间,团队内部有过好几次给力的分享,这里对西风师傅分享的继承机制稍作整理一下,适当加了些口语化的描述,留作备案. 一.讲个故事吧 澄清在先,Java和Javascript是雷锋和雷峰塔的关系.Ja ...

随机推荐

  1. Jmeter学习(三十三)调试工具Debug Sampler

    一.Debug Sampler介绍: 使用Jmeter开发脚本时,难免需要调试,这时可以使用Jmeter的Debug Sampler,它有三个选项:JMeter properties,JMeter v ...

  2. RDIFramework.NET-.NET快速信息化系统开发整合框架 【开发实例 EasyUI】之产品管理(MVC版)

    RDIFramework.NET—.NET快速开发整合框架 [开发实例]之产品管理(MVC版) 接上篇:RDIFramework.NET (.NET快速信息化系统开发整合框架) [开发实例]之产品管理 ...

  3. Linux -- objdump二进制文件比较

    objdump工具用来显示二进制文件的信息,就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息. 常用参数说明 -f 显示文件头信息 -D 反汇编所有section (-d反汇编特定se ...

  4. noi 04:求整数的和与均值

    04:求整数的和与均值 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 读入n(1 <= n <= 10000)个整数,求它们的和与均值. 输入 ...

  5. Url通配符映射

    原文:http://www.cnblogs.com/liukemng/p/3726897.ht 1.URL路径映射 1.1.对一个action配置多个URL映射: 我们把上一篇中的HelloWorld ...

  6. java随笔

    java与c++的几点区别 (1)Java比C++程序可靠性更高.有人曾估计每50行C++程序中至少有一个BUG.姑且不去讨论这个数字是否夸张,但是任何一个C++程序员都不得不承认C++语言在提供强大 ...

  7. UEFI引导在GPT分区下安装win2008——抓住那只傲娇的win2008

    上周遇到个客户DELL R520的服务器新采购了8块3T硬盘做备份服务器,raid配置5+1,一个磁21.8T.先用普通的装desktop OS的方法发现进去没raid盘,然后就按照官方的文档进入Li ...

  8. 2.1 C#的关键字

    关键字是被C#规定了用途的重要单词.在visual studio中,关键字以蓝色字体显示.图中红色方框圈起来的就是关键字. 关键字 class ,这个关键字的用途是声明类.比如上面例子中,类名叫做Pr ...

  9. 最好的cpm广告联盟哪里有

    最好的cpm广告联盟哪里有,58传媒广告联盟还要提醒众位站长的是网站在经营发展中必须最大化的扩展自己的优势力量.每个网站都有属于自己的优势魅力,这些优势特点只有得到最大化的发挥才能为网站带来意想不到的 ...

  10. 夺命雷公狗----Git---2---基本用法

    首先我们来创建一个git仓库,其实命令也是很简单的.. git init 其实在创建完成后,该目录会多了一个.git的文件夹,但该文件夹是隐藏的.... 其实这就是一个git仓库,特别建议不要在这个目 ...