继承是面向对象的语言中,一个最为津津乐道并乐此不疲的话题之一。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. Jquery实现鼠标拖拽效果

    <%@ page language="java" import="java.util.*" pageEncoding="utf-8"% ...

  2. 修改maven本地仓库的位置

  3. Eclipse perl的IDE环境插件-EPIC

    前提:1.安装好perl环境:ActivePerl(验证方法:cmd中输入 perl -v 看是否有反应~) 2.安装Eclipse 3.0以上版本 可选:安装PadWalker包,主要是全局变量跟踪 ...

  4. bootstrap switch功能

    bootstrap switch是一个按钮开关,点击时获取其状态可通过以下代码: <input id="email_switch_state" type="chec ...

  5. vs2012中的小技巧

    解除起始页: 网站(或者叫项目)-属性-启动选项-使用当前页 发布项目: 有些文件在发布的时候,不能发布到指定文件夹中,所以要手动修改该文件的属性 修改两处: 复制到输出目录:始终复制 生成操作:内容

  6. MySQL导入sql脚本中文乱码设置和常用命令

    1. use database_name; 2. set names utf8; (或其他需要的编码) 3. source example.sql (sql文件存放路径) Mysql安装目录数据库目录 ...

  7. 使用flexbox来布局web应用

    使用 flexbox 可以帮助你设计出引人注目的布局,并且在pc端或移动端能够很好的缩放.告别使用浮动的 <div> 元素.绝对定位 和一些JavaScript hacks, 使用仅仅几行 ...

  8. jdk8 之 java.time包AND DateUtils

    package com.jansh.comm.util; import java.time.Clock; import java.time.LocalDate; import java.time.Lo ...

  9. Leetcode 181. Employees Earning More Than Their Managers

    The Employee table holds all employees including their managers. Every employee has an Id, and there ...

  10. HTML5学习笔记三:aside元素,time元素与微格式

    一.aside元素 表示当前页面或文章的附属信息部分,相关的引用,侧边栏,广告等有别于主要内容的部分:主要有一下两种用法: 1. 被包含在article元素中作为主要内容的附属信息部分,可以是与当前文 ...