JavaScript有多种继承模式,总结起来用到的方法有:原型链的传递、构造函数的借用、对象的复制。

 

对于原型链和原型的解释,可参考:JavaScript 原型中的哲学思想
这篇文章讲得很清晰,让我们明白:所有JS对象源于null,并通过原型指针和原型对象来实现继承。

构造函数和原型对象的关系如下:

 

每个构造函数都有一个prototype属性,指向函数的原型对象;原型对象中又有一个constructor属性,重新指向构造函数。而对象实例中有一个原型指针[[prototype]](在Firefox、Safari和Chrome中,对应属性proto),指向原型对象。
明白原型之后,就进入正题继承了。

1.原型链继承

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

 

通过SubType.prototype=new SuperType(),实现了以下三点:
A:重写了SubType的原型,让子类原型和子类构造函数之间断开联系。
B:子类原型是父类的实例,其原型指针[[prototype]]指向了父类的原型对象,这样子类就可以沿着原型链访问到父类的方法getSuperValue()。
C:子类原型是父类实例,通过父类构造函数,子类原型继承了父类的属性superproperty。
最终,子类继承了父类的方法和属性。
原型链继承的问题:
父类的实例属性成了子类的原型属性,如上面的superproperty,会被子类所有实例共享。该属性是基本类型值时没有问题,但如果是引用类型值(比如数组),那么修改实例1的该属性(比如向数组push一个新值),实例2也会跟着改变。
也就是说,实例们只有共性,不能保持个性。

2.构造函数继承

function SuperType(name){
this.name = name;
}
function SubType(){
//继承了SuperType,同时还传递了参数
SuperType.call(this,"Nicholas");
this.age = 29;
}
var instance = new SubType();
alert(instance.name); //"Nicholas"
alert(instance.age); //29

 

通过SuperType.call(this),在子类构造函数中调用了父类的构造函数,创建子类实例会执行子类的构造函数(含父类的构造函数),也就完成了继承。当然,子类和父类的原型是没有关系的,子类实例不能访问父类原型对象中的属性和方法。
构造函数继承的问题:
方法在构造函数中定义,无法实现函数复用。比如父类中有一个方法getName(),则每次创建子类实例的时候,都要创建一个新的getName(),通过instance1.getName() !== instance.getName()就可以验证这一点。
这就是说,实例们保持了个性,却不能共享方法。

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); //第二次调用SuperTyper()
this.age = age;
}
//继承方法
SubType.prototype = new SuperType(); //第一次调用SuperTyper()
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

 

通过借用构造函数来继承属性,原型链来继承方法。结合了两者的优点,让实例们即保持个性,又共享方法。
组合继承的问题:
两次调用父类的构造函数。第一次(A):SubType.prototype=new SuperType(),子类原型对象取得了父类的实例属性。第二次(B):
SuperType.call(this),创建子类实例时,调用父类构造函数,重写实例属性,屏蔽了原型对象上同名属性。

4.原型式继承

function object(o){
function F(){};
F.prototype = o;
return new F();
}
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()函数进行浅复制,再将得到的对象实例加以修改。
可以看到,这种继承方法是没有父类和子类的,只是通过复制对象来得到副本。
ES5有object.create()方法,是object()的规范化,可以传入两个参数:要复制的对象和额外的属性对象(如{name:{value:Greg}},这种方式会覆盖基础对象上的同名属性)。
原型式继承的问题:
和原型链继承一样,继承的属性由所有实例共享,改动一个实例的引用类型值时,所有实例都会改变。

5.寄生式继承

function createAnother(original){
var clone = object(original);//object()函数创建对象
clone.sayHi = function(){ //增强这个对象
alert("hi");
};
return clone; //返回这个对象
}
var person = {
name:"Nicholas";
friends:["Shelby","Court","Van"];
} //基础对象
var anotherPerson = createAnother(person); //新对象
anotherPerson.sayHi(); //"hi"

寄生式和原型式方法相同,都是复制一个基础对象来得到新对象,不同的是它将对象实例的修改放到也放到函数中,将整个过程(创建、增强、返回)封装了起来。

6.寄生组合式继承

function inheritPrototype(subType,superType){
var prototype = object(superType.prototype); //创建对象
prototype.constructor = subType; //增强对象
subType.prototype = 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); //只调用一次SuperTyper()
this.age = age;
}
//继承方法
inheritPrototype(SubType,SuperType);
SubType.prototype.sayAge = function(){
alert(this.age);
}

 

顾名思义,这种继承模式就是寄生式(复制)+组合式(原型链+构造函数),将几种方法组合起来。解决了组合式继承两次调用父类构造函数的问题。

代码来源:
《JavaScript 高级程序设计》

文/minxuan(简书作者)
原文链接:http://www.jianshu.com/p/0045cd01e0be
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

JavaScript继承(图解笔记)的更多相关文章

  1. JavaScript继承学习笔记

    JavaScript作为一个面向对象语言(JS是基于对象的),可以实现继承是必不可少的,但是由于本身并没有类的概念,所以不会像真正的面向对象编程语言通过类实现继承,但可以通过其他方法实现继承.(jav ...

  2. 图解JavaScript 继承

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

  3. 【转载】Javascript原型继承-学习笔记

    阮一峰这篇文章写的很好 http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javas ...

  4. 【读书笔记】javascript 继承

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

  5. JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)

    JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...

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

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

  7. JavaScript语言精粹笔记

    JavaScript语言精粹笔记 掌握语言的每个特性可以让你出风头,但是并不推荐,因为一部分的特性带来的麻烦可能远超本身的价值.正如书中所言,坏的材料并不能雕刻出好的作品,要成为一名更好的程序员,要取 ...

  8. 转:Javascript继承机制的设计思想

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

  9. 《你不知道的javascript》读书笔记2

    概述 放假读完了<你不知道的javascript>上篇,学到了很多东西,记录下来,供以后开发时参考,相信对其他人也有用. 这篇笔记是这本书的下半部分,上半部分请见<你不知道的java ...

  10. Javascript MVC 学习笔记(一) 模型和数据

    写在前面 近期在看<MVC的Javascript富应用开发>一书.本来是抱着一口气读完的想法去看的.结果才看了一点就傻眼了:太多不懂的地方了. 仅仅好看一点查一点,一点一点往下看吧,进度虽 ...

随机推荐

  1. Ext JS表单Ext.form.FormPanel

    1.表单 对于传统的b/s应用来说,数据录入元素是放在表单<form>标签里面的.而对于ExtJS应用来说,则可以直接使用FormPanel控件来存放表单中的元素.FormPanel继承自 ...

  2. poj 2253 Frogger (最短路径)

    Frogger Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 22557   Accepted: 7339 Descript ...

  3. [bzoj2038] [2009国家集训队]小Z的袜子

    Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只 ...

  4. Codeforces Round #428 (Div. 2) B

    B. Game of the Rows(贪心) 题意: 有k种颜色,每种有\(a_i\)个,将这k种颜色放在一个\(n * 8格子里\) 放置规则不能出现两个不同颜色在相邻的格子里,相邻的定义为在同一 ...

  5. 雅礼集训 Day6 T1 Merchant 解题报告

    Merchant 题目描述 有\(n\)个物品,第\(i\)个物品有两个属性\(k_i,b_i\),表示它在时刻\(x\)的价值为\(k_i\times x+b_i\). 当前处于时刻\(0\),你可 ...

  6. 洛谷 P3302 [SDOI2013]森林 解题报告

    P3302 [SDOI2013]森林 题目描述 小\(Z\)有一片森林,含有\(N\)个节点,每个节点上都有一个非负整数作为权值.初始的时候,森林中有\(M\)条边. 小Z希望执行\(T\)个操作,操 ...

  7. 262144 (game)

    262144 (game) 题目描述 Bessie likes downloading games to play on her cell phone, even though she does fi ...

  8. 类复制 MemberwiseClone与Clone(深 浅 Clone)

    MemberwiseClone 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象.如果字段是值类型的,则对该字段执行逐位复制.如果字段是引用类型,则复制引用但 ...

  9. Audio Unit 介绍

    关于 Audio Unit iOS 提供了音频处理插件,支持混音,声音均衡,格式转化,以及用于录音,回放,离线渲染,实时对话的输入输出.可以动态载入和使用这些强大而灵活的插件,在 iOS 应用中这些插 ...

  10. spring in action 学习笔记十:用@PropertySource避免注入外部属性的值硬代码化

    @PropertySource的写法为:@PropertySource("classpath:某个.properties文件的类路径") 首先来看一下这个案例的目录结构,重点看带红 ...