继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一。JAVASCRIPT中的继承,主要是依靠原型链来实现的。上一篇文章介绍过,JAVASCRIPT中,每一个对象都有一个prototype属性,这个属性指向一个原型对象(原型对象包含了所有指向它的对象共享的属性和方法,默认是Object)。那么,如果我们让原型对象等于一个类型的实例,那么结果会怎么样呢?显然,此时的原型对象将包含指向另一个原型的指针;假如另一个原型又是另一个类型的实例,如此层层递进,就构成了原型链。这就是原型链的基本概念。

  以下是一个原型链的简单例子:

 // 定义一个对象
 function superType(){
     this.property = true;
 }
 // 定义对象共享的方法
 superType.prototype.getSuperValue = function(){
     return this.property;
 }
 // 定义另外一个对象
 function subType(){

 }
 // 通过原型链的方式(让对象subType指向superType的一个实例),让subType继承superType
 subType.prototype = new superType();

 var instance = new subType();
 alert(instance.getSuperValue());  //true

  JAVASCRIPT不仅提供了函数来确认原型和实例之间的关系,也允许子类重写超类的方法:

 // 由于原型链的关系,我们可以说,instance 是 Object, SuperType, SubType中任何一个类型的实例,因此,以下结果,均返回true
 // instanceof方法
 alert(instance instanceof Object);
 alert(instance instanceof SuperType);
 alert(instance instanceof SubType);

 // isPrototypeOf方法
 alert(Object.prototype.isPrototypeOf(instance));
 alert(SuperType.prototype.isPrototypeOf(instance));
 alert(SubType.prototype.isPrototypeOf(instance));

  

 // 定义一个对象
 function superType(){
     this.property = true;
 }
 // 定义对象共享的方法
 superType.prototype.getSuperValue = function(){
     return this.property;
 }
 // 定义另外一个对象
 function subType(){

 }
 // 通过原型链的方式(让对象subType指向superType的一个实例),让subType继承superType
 subType.prototype = new superType();

 var instance = new subType();

 // 重写超类中的方法
 subType.prototype.getSuperValue = function(){
     return false;
 }
 // 注意,不能使用下面的字面量的方式来来添加方法,因为这种方式会切断原型链,让subType和superType没有联系
 /*subType.prototype = {
     getSuperVaue:function(){
         return false
     }
 };*/
 alert(instance.getSuperValue());  //false

  想必大家还记得,我们前面介绍过的包含所有引用类型的原型属性,会被所有实例所共享;这也是为什么要在构造函数中定义属性,而不在原型中定义属性的原因;在通过原型链来继承时,也会出现相同的问题——当有实例修改了原型的某个引用类型的属性时,所有实例的这个属性均会被修改。由于这个问题,实践中很少单独使用原型链,而采用借用构造函数、组合继承、原型式继承、寄生式继承、寄生组合式继承的方式。

一、借用构造函数

  借用构造函数是在子类型中调用超类型的构造函数,注意此时的superType并不是subType的原型。

 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

 alert(instance1 instanceof superType); //false

  借用构造函数所有的方法都在构造函数中定义,因此函数的复用便无从谈起了;在实践中,很少单独使用借用构造函数,而是将原型链和借用构造函数一起使用,接下来介绍的组合继承就是这种方式。

二、组合继承

组合继承是原型链和构造函数的技术组合到一块儿,从而发挥二者之长的一种继承模式。其背后的思路是使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承。这也是JAVASCRIPT中最常见的继承模式。

 function superType(name){
     this.name = name;
     this.colors = ["red","blue","green"];
 }
 superType.prototype.sayName = function(){
     alert(this.name);
 }

 function subType(name){
     // 继承属性,同时也传递了自己的参数
     superType.call(this, name);
 }

 // 继承方法
 subType.prototype = new superType();

 var instance1 = new subType("Lillian");
 instance1.colors.push("black");
 alert(instance1.colors);//red,blue,green,black
 instance1.sayName(); //Lillian

 var instance2 = new subType("Matthew");
 alert(instance2.colors);//red,blue,green
 instance2.sayName();//Matthew

 alert(instance1 instanceof superType); //true

  组合继承也有自己的不足:无论在什么情况下,都会调用两次超类的构造函数(例子中标红的部分),一次是在创建子类原型的时候,另一次是在子类构造函数内部。后面我们将介绍的寄生组合继承会避免这个问题。

三、原型继承

  这种方式与使用原型链继承类似,所有的实例会共享引用类型的属性以及方法。在以下例子中,是通过一个叫做object111的函数来实现的,在object111函数的内部,先创建了一个临时性的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的一个新实例。

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

 var person = {
     friends:["Lillian", "Matthew", "Susan"]
 };

 var person1 = object111(person);
 person1.friends.push("111");
 var person2 = object111(person);
 person2.friends.push("222");
 alert(person1.friends); //Lillian,Matthew,Susan,111,222
 alert(person1.friends);  //Lillian,Matthew,Susan,111,222

四、寄生式继承

  寄生式继承与原型继承在格式上很像,也是创建了一个用于封装继承过程的函数,只是这个函数中没有定义构造函数。

 function createAnother(original){
     // 这里的object111函数,可以使用任何能够返回新对象的函数替代
     var clone = object111(original);
     clone.sayHi = function(){
         alert("Hi");
     };
     return clone;
 }

 var person = {
     friends:["Lillian", "Matthew", "Susan"]
 };

 var person1 = createAnother(person);
 person1.sayHi();

五、寄生组合继承

  和组合继承相比,寄生组合继承也是调用借用构造函数来继承属性,但并不通过指定子类型的原型来继承,而是获取超类的一个副本。它比组合继承效率高的地方在于,只调用了一次超类的构造函数,而我们前面介绍的组合继承,调用了两次。

 function object111(o){
     function F(){};
     F.prototype = o;
     return new F();
 }
 function inheritPrototype(subType, superType){
     // 这里的object111函数,可以使用任何能够返回新对象的函数替代
     var prototype = object111(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){
     // 继承属性,同时也传递了自己的参数
      superType.call(this, name);
 }
 inheritPrototype(subType, superType)
 var person1 = new subType("Lillian");
 person1.sayName();

小结:

  JAVASCRIPT主要是通过原型链进行的继承,原型链的构建是通过将一个类型的实例赋值给另一个对象的原型来实现的。原型链的问题是,所有对象的实例共享继承的属性和方法,因此不适合单独使用;通过构建多种继承模式,解决了这个问题。

《JAVASCRIPT高级程序设计》根植于原型链的继承的更多相关文章

  1. JavaScript高级内容:原型链、继承、执行上下文、作用域链、闭包

    了解这些问题,我先一步步来看,先从基础说起,然后引出这些概念. 本文只用实例验证结果,并做简要说明,给大家增加些印象,因为单独一项拿出来都需要大篇幅讲解. 1.值类型 & 引用类型 funct ...

  2. 读javascript高级程序设计06-面向对象之继承

    原型链是实现继承的主要方法,通过原型能让一个引用类型继承另一个引用类型. 1.原型链实现继承 function SuperType(){ this.superprop=1; } SuperType.p ...

  3. 《Javascript高级程序设计》读书笔记之继承

    1.原型链继承 让构造函数的原型对象等于另一个类型的实例,利用原型让一个引用类型继承另一个引用类型的属性和方法 function SuperType() { this.property=true; } ...

  4. JavaScript高级程序设计之原型对象

    构造函数.原型对象.构造器是一体的关系,同时产生: 实例中的隐藏属性__proto__指向原型对象: 原型对象是这四种关系的纽带. 原型对象是动态的,不论在何处变化,实例中可以立即体现出来. var ...

  5. JavaScript高级程序设计之作用域链

    JavaScript只有函数作用域:每个函数都有个作用域链直达window对象. 变量的查找由内而外层层查找,找到即止. 同时不仅可以查找使用,甚至可以改变外部变量. var color = &quo ...

  6. JavaScript中的原型链和继承

    理解原型链 在 JavaScript 的世界中,函数是一等公民. 上面这句话在很多地方都看到过.用我自己的话来理解就是:函数既当爹又当妈."当爹"是因为我们用函数去处理各种&quo ...

  7. 读javascript高级程序设计00-目录

    javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...

  8. 读javascript高级程序设计-目录

    javascript高级编程读书笔记系列,也是本砖头书.感觉js是一种很好上手的语言,不过本书细细读来发现了很多之前不了解的细节,受益良多.<br/>本笔记是为了方便日后查阅,仅作学习交流 ...

  9. 【转】js原型链与继承

    原文链接:https://blog.csdn.net/u012468376/article/details/53127929 一.继承的概念 ​ 继承是所有的面向对象的语言最重要的特征之一.大部分的o ...

随机推荐

  1. 方法的标签_With携带

    方法中参数的标签: 标签的由来:1.标签也是方法名的一部分:2.为了提高程序的阅读性:OC方法允许我们给每个参数添加一个标签来说明当前参数的含义: 标签的作用:标签是为了标识变量的,因此标签名和变量名 ...

  2. Attribute name invalid for tag form according to TLD异常解决办法_gaigai_百度空间

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  3. Android系统属性SystemProperties分析

    下面这几个博客总结的不错,有空看下: http://www.cnblogs.com/bastard/archive/2012/10/11/2720314.html http://blog.csdn.n ...

  4. $(function(){})的执行过程分析

    作者:zccst 首先,$(function(){})是$(document).ready(function(){})的简写形式. 在日常使用中,我们会把代码写到$(function(){})中,今天 ...

  5. BZOJ 1106: [POI2007]立方体大作战tet

    1106: [POI2007]立方体大作战tet Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 682  Solved: 496[Submit][St ...

  6. iOS开发——自定义进度圆环

    1.在DrawCircle.h文件中 提供了接口,在使用的时候,可以设定圆心.半径.角度.圆环的宽度.圆环的背景底色.圆环的进度条颜色,当然后面三个有自定义的值. // //  DrawCircle. ...

  7. 源码解析-knockout源码准备

    准备包括心理和资源两方面. 心理 我看过一句话说,当你用一个框架时,不要忙着看一遍使用教程就开始写项目,先去看看框架原理. 这句话我深以为然.现今前端快速发展,很多前端攻城狮都很茫然:框架更新太快了, ...

  8. N皇后问题——递归求解

    比较简单,废话不说,上代码: public class NQueen { //比如:position[1]=3,表示第一行的第三列有一个皇后 private int [] position; //总的 ...

  9. 将ADS1.2的工程迁移到KEIL上-基于2440

    裸机程序应该是一个很好的选择 1. 不拷贝启动代码,因为我们用自己的启动代码 2.       建立工程目录分级,建立完成后如下所示 拷贝相应代码到对应目录中 Option中拷贝 Core中拷贝 建立 ...

  10. FastJson的用法

    一.简单数据的序列化 pubic class UserInfo implements Serializable{ private String name; private int age; publi ...