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

 

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

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

 

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

1.原型链继承

  1. function SuperType(){
  2. this.superproperty = true;
  3. }
  4. SuperType.prototype.getSuperValue = function(){
  5. return this.superproperty;
  6. }
  7. function SubType(){
  8. this.subproperty = false;
  9. }
  10. SubType.prototype = new SuperType(); //原型链继承
  11. SubType.prototype.getSubValue =function(){
  12. return this.subproperty;
  13. }
  14. var instance = new SubType();
  15. alert(instance.getSuperValue());//true

 

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

2.构造函数继承

  1. function SuperType(name){
  2. this.name = name;
  3. }
  4. function SubType(){
  5. //继承了SuperType,同时还传递了参数
  6. SuperType.call(this,"Nicholas");
  7. this.age = 29;
  8. }
  9. var instance = new SubType();
  10. alert(instance.name); //"Nicholas"
  11. alert(instance.age); //29

 

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

3.组合继承

  1. function SuperType(name){
  2. this.name = name;
  3. this.colors = ["red","blue","green"];
  4. }
  5. SuperType.prototype.sayName = function(){
  6. alert(this.name);
  7. }
  8. function SubType(name,age){
  9. //继承属性
  10. SuperType.call(this,name); //第二次调用SuperTyper()
  11. this.age = age;
  12. }
  13. //继承方法
  14. SubType.prototype = new SuperType(); //第一次调用SuperTyper()
  15. SubType.prototype.constructor = SubType;
  16. SubType.prototype.sayAge = function(){
  17. alert(this.age);
  18. }
  19. var instance1 = new SubType("Nicholas",29);
  20. instance1.colors.push("black");
  21. alert(instance1.colors); // "red","blue","green","black"
  22. instance1.sayName(); //"Nicholas"
  23. instance1.sayAge(); //29
  24. var instance2 = new SubType("Greg",27);
  25. alert(instance2.colors); // "red","blue","green"
  26. instance2.sayName(); //"Greg"
  27. instance2.sayAge(); //27

 

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

4.原型式继承

  1. function object(o){
  2. function F(){};
  3. F.prototype = o;
  4. return new F();
  5. }
  6. var person = {
  7. name:"Nicholas",
  8. friends:["Shelby","Court","Van"]
  9. };
  10. var anotherPerson = object(person);
  11. anotherPerson.name = "Greg";
  12. anotherPerson.friends.push("Rob");
  13. var yetAnotherPerson = object(person);
  14. yetAnotherPerson.name = "Linda";
  15. yetAnotherPerson.friends.push("Barbie");
  16. alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

 

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

5.寄生式继承

  1. function createAnother(original){
  2. var clone = object(original);//object()函数创建对象
  3. clone.sayHi = function(){ //增强这个对象
  4. alert("hi");
  5. };
  6. return clone; //返回这个对象
  7. }
  8. var person = {
  9. name:"Nicholas";
  10. friends:["Shelby","Court","Van"];
  11. } //基础对象
  12. var anotherPerson = createAnother(person); //新对象
  13. anotherPerson.sayHi(); //"hi"

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

6.寄生组合式继承

  1. function inheritPrototype(subType,superType){
  2. var prototype = object(superType.prototype); //创建对象
  3. prototype.constructor = subType; //增强对象
  4. subType.prototype = prototype; //指定对象
  5. }
  6. function SuperType(name){
  7. this.name = name;
  8. this.colors = ["red","blue","green"];
  9. }
  10. SuperType.prototype.sayName = function(){
  11. alert(this.name);
  12. }
  13. function SubType(name,age){
  14. //继承属性
  15. SuperType.call(this,name); //只调用一次SuperTyper()
  16. this.age = age;
  17. }
  18. //继承方法
  19. inheritPrototype(SubType,SuperType);
  20. SubType.prototype.sayAge = function(){
  21. alert(this.age);
  22. }

 

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

代码来源:
《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. 第十四篇:JavaScript

    本篇内容 简介 使用 DOM 一. 简介 JavaScript 是世界上最流行的编程语言. 这门语言可用于 HTML 和 web,更可广泛用于服务器.PC.笔记本电脑.平板电脑和智能手机等设备. Ja ...

  2. POJ 2142 The balance | EXGCD

    题目: 求ax+by=c的一组解,使得abs(x)+abs(y)尽量小,满足前面前提下abs(ax)+abs(by)尽量小 题解: exgcd之后,分别求出让x尽量小和y尽量小的解,取min即可 #i ...

  3. css实现0.5像素

    .border{ position: relative; } .border:before{ content: ''; position: absolute; width: 200%; height: ...

  4. 做一个类似JQuery获取DOM对象的$()

    在dom操作时,有时根据id获取单个对象.有时根据className获取多个对象.平常可能我们用两个函数来实现这两个功能.不过我将它们整合了一下,目前使用情况良好,函数如下: view source ...

  5. AtCoder Regular Contest 093 E: Bichrome Spanning Tree(生成树)

    Bichrome Spanning Tree 题意: 给出一个n个点,m条边的无向连通图,现在要给每条边染色,可以染成黑色或者白色. 现在要求在染色完毕后,找出一个至少包含一条黑边和一条白边的最小生成 ...

  6. DP———5.拣馅饼(记忆化搜索,得到最大值)

    都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼.说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内.馅饼如果掉在了地上当然就 ...

  7. swiper伸缩侧边菜单栏

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  8. display:inline、block、inline-block的区别 摘】

    display:block就是将元素显示为块级元素. block元素的特点是: 总是在新行上开始: 高度,行高以及顶和底边距都可控制: 宽度缺省是它的容器的100%,除非设定一个宽度 <div& ...

  9. matlab 命令行快捷键

  10. POCO库中文编程参考指南(5)Poco::Net::SocketAddress

    1 枚举 最大地址长度,这个与Poco::Net::IPAddress中的定义可以类比,不过这里指的是`struct sockaddr_in6 enum { MAX_ADDRESS_LENGTH = ...