JavaScript常见继承模式实例小结

本文实例总结了JavaScript常见继承模式。分享给大家供大家参考,具体如下:

JavaScript中并没有传统的面向对象语言中的类的概念,但是却实现了特殊的继承机制。

(阅读此文您首先需要知道原型的知识)

先来说说第一种继承方式,原型链继承。

一. 原型链继承

所谓原型链继承,就是让父类的一个实例作为子类的原型。

即 :

parentInstance = new Parent();
child.prototype = parentInstance;

这样,在创建子类的实例时,子类实例的__proto__指向父类的实例(即此时子类构造函数的prototype属性),而父类实例的__proto__又指向父类构造函数的prototype属性。借用这种方式形成了一条原型链。

由于JavaScript中搜索实例中调用的变量有如下方式:

  1. 在当前实例中寻找变量名
  2. 在当前实例所指向的原型中寻找

假设原型链中有如下继承关系:

grandparent(有方法 grandparent.prototype.sayHello) -> parent -> child

当在child的实例child_ming调用方法 sayHello 时,首先在child_ming中(即只定义在child_ming这一个实例中,而非所有实例中)搜索sayHello,并未找到,然后开始搜索它所指向的原型,即parent的实例。在parent的实例中也没有此方法,开始搜索parent的原型,即grandparent的实例。在grandparent的实例中依然没有找到,又搜索grandparent的原型并找到该方法。

可以看出,这样便实现了继承。

如同在使用prototype创建对象时遇到的问题,倘若完全使用原型链进行继承,会使得一些需要继承但不需要在不同实例间进行共享的属性变得不方便实现。

下面就要说一说借用构造函数实现的继承。

二. 借用构造函数实现继承

所谓借用构造函数实现继承,即在子类的构造函数中把父类的构造函数借来使用,以求在子类中生成父类的属性。

看如下代码:

function Parent(color){
  this.color = color;
  this.getColor = function(){ alert(this.color); };
}
function Child(color,size){
  Parent.call(this,color);
  this.size = size;
}

这就是一个简单的借用构造函数的继承。

通过使用父类的构造函数,可以使得同一构造函数的不同的实例的同一属性拥有不同的值。解决了原型链继承中的属性共享的弊端。

然而,如同使用构造函数创建对象时遇到的问题,通过构造函数生成的方法面临着重复定义的问题,同一类下的不同实例拥有各自的方法,而一般来讲方法是需要实现复用的,没有必要让它们拥有各自的方法。

使用组合继承可以解决这个问题。

三. 组合继承(原型链与借用构造函数)

既然原型链可以实现属性共享,借用构造函数可以实现属性值的私有,不妨将它们结合起来,这就形成了组合继承。

所谓组合继承,实际上就是用一次原型链继承,用一次借用构造函数继承。

看如下代码:

function Parent(color){
  this.color = color;
}
Parent.prototype.getColor = function(){ alert(this.color); };
function Child(color,size){
  Parent.call(this,color); //借用构造函数继承属性
  this.size = size;
}
//下面的属性'green'并没有影响,这里要使用原型继承方法
Child.prototype = new Parent('green');
var child_demo = new Child('red',200);

首先思考这样两个问题,创建一个child 实例到底生成了几份color 属性?在代码中既定义了'red' 又定义了 ‘green',当调用 child_demo.getColor() 时,到底会alert 哪一个?

首先来看第一个问题。由于子类构造函数中借用了父类的构造函数,在创建子类实例时必然会生成一次color 属性。但是不要忘记,我们在继承方法时是让子类构造函数的原型指向一个父类的实例,在创建这个父类实例时还要生成一次color 属性(即上面'green'处),而这个属性是完全没有必要存在的。所以一共生成了两份color 属性,一个有用一个没用。

再来看第二个问题。只要能理解this 的含义就可以知道:

child_demo.getColor() // 'red'
Child.prototype.getColor() //'green'

组合继承结合了前两种继承方式的优点,但它也有自己的缺点。从生成两份color 属性可以知道在继承过程中调用了两次Parent 的构造函数,这会造成执行完成速度的问题,影响了效率。但是瑕不掩瑜,这种继承方式还是成为了JavaScript中最常用的继承模式。

四. 原型式继承

原型式继承是从已有对象的基础上继承,基于已有对象创建新的对象。

看如下代码:

var obj = {
  color: 'red',
  getColor: function(){ alert(this.color); },
};
//getChild(obj)返回的是一个__proto__指向obj的实例
function getChild(obj){
  function func(){}
  func.prototype = obj;
  return new func();
}
var child_demo = getChild(obj);

这种继承方式与原型链式的继承方式有相同点和不同点。

相同点:它们都是通过改变子类构造函数的原型属性来实现继承,所继承的属性都具有不同实例共享的特点。

不同点:原型链继承中子类构造函数的原型(prototype)是父类的一个实例(我们真正需要继承的东西可能存在于父类构造函数的原型中,也可能存在于直接指向的父类实例中),而原型式继承中子类构造函数的原型是一个已有的对象,可以说直接就是父类。

五. 寄生式继承

寄生式继承可以说是原型式继承的变体,它对原型式继承进行了封装,使得创建子类实例只依赖于一个函数。

看如下代码:

var obj = {
  color: 'red',
  getColor: function(){ alert(this.color); },
};
function getChild(obj){
  function func(){}
  func.prototype = obj;
  return new func();
}
/*-------以上是原型式继承的代码----------*/
function betterGetChild(obj,size){
  var temp_obj = getChild(obj);
  temp_obj.size = size;
  temp_obj.getSize = function(){ return this.size; };
  return temp_obj;
}
var demo = betterGetChild(obj,200);

六. 寄生组合式继承

寄生组合式是组合继承加上原型式继承的应用。由于组合式继承中子类构造函数的原型指向父类的一个实例而非父类构造函数的原型,会导致设置子类构造函数的原型时对父类的构造函数进行一次额外的调用。所以在寄生式组合继承中,借用原型式继承的思想,将父类构造函数的原型当作一个已有对象,让子类构造函数的原型直接指向它。

看如下代码:

function getChild(obj){
  function func(){}
  func.prototype = obj;
  return new func();  //调用构造函数
}
/*-------以上是原型式继承的代码----------*/
function Parent(color){
  this.color = color;
}
Parent.prototype.getColor = function(){
  alert(this.color);
};
function Child(color,size){
  Parent.call(this,color); //调用构造函数
  this.size = size;
}
//Child.prototype直接指向一个__proto__指向Parent.prototype的实例
Child.prototype = getChild(Parent.prototype);
Child.prototype.getSize = function(){ alert(this.size); };

其实严格来说,这种方式也调用了两次构造函数,但是其中一次构造函数的调用是对一个空函数的调用,而不是两次都调用父类的构造函数。

在组合继承中,在子类的原型中其实还保存有父类的没有定义在原型中的属性(由于子类构造函数的原型是父类的一个实例),只不过是子类构造函数在借用父类构造函数在当前实例中生成了覆盖原型中那些属性的属性。而寄生式组合继承中,根本不会产生那些冗余数据。

人们普遍认为寄生时组合继承是最理想的继承方式。

更多关于JavaScript相关内容还可查看本站专题:《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

(0)

相关推荐

  • js中继承的几种用法总结(apply,call,prototype)

    一,js中对象继承 js中有三种继承方式 1.js原型(prototype)实现继承 复制代码 代码如下: <SPAN style="BACKGROUND-COLOR: #ffffff"><SPAN style="FONT-SIZE: 18px"><html>  <body>  <script type="text/javascript">      function Person(na

  • 实例介绍JavaScript中多种组合继承

    1. 组合继承:又叫伪经典继承,是指将原型链和借用构造函数技术组合在一块的一种继承方式. 下面来看一个例子: function SuperType(name) { this.name = name; this.colors = ["red", "blue", "green"]; } SuperType.prototype.sayName = function() { alert(this.name); } function SubType(nam

  • JavaScript继承与聚合实例详解

    本文实例讲述了JavaScript继承与聚合.分享给大家供大家参考,具体如下: 一.继承 第一种方式:类与被继承类直接耦合度高 1. 首先,准备一个可以被继承的类(父类),例如 //创建一个人员类 function Person(name) {//现在Person里面的域是由Person里面的 来this来控制的 this.name=name; } 2. 然后,有个需要继承父类的子类 function Teacher(name,books) { Person.call(this,name);//

  • 彻底理解js面向对象之继承

    说道这个继承,了解object-oriented的朋友都知道,大多oo语言都有两种,一种是接口继承(只继承方法签名):一种是实现继承(继承实际的方法) 奈何js中没有签名,因而只有实现继承,而且靠的是原型链实现的.下面正式的说一说js中继承那点事儿 1.原型链 原型链:实现继承的主要方法,利用原型让一个引用类型继承另一个引用类型的属性和方法. 回顾:构造函数,原型,实例三者的关系 每一个构造函数都有一个原型对象(Person.prototype):原型对象都包含指向构造函数的指针(constru

  • JavaScript是如何实现继承的(六种方式)

    前言:大多OO语言都支持两种继承方式: 接口继承和实现继承 ,而ECMAScript中无法实现接口继承,ECMAScript只支持实现继承,而且其实现继承主要是依靠 原型链 来实现. 1.原型链 基本思想:利用原型让一个引用类型继承另外一个引用类型的属性和方法. 构造函数,原型,实例之间的关系:每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针. 原型链实现继承例子: function SuperType() { this.property

  • JavaScript寄生组合式继承原理与用法分析

    本文实例讲述了JavaScript寄生组合式继承.分享给大家供大家参考,具体如下: 寄生组合式继承 寄生组合式继承,就是通过伪造对象来继承属性,通过原型链的混成形式来继承方法. 这种技术的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,因为我们所需要的只是超类型原型的一个副本而已(这一点正是组合继承的问题,并且正是JavaScript这种原型式语言的强大之处,直接copy对象-原型对象).本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型. function

  • 15分钟深入了解JS继承分类、原理与用法

    本文全面讲述了JS继承分类.原理与用法.分享给大家供大家参考,具体如下: 许多 OO 语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法.由于 ECMAScript 中的函数没有签名,所以在 JS 中无法实现接口继承.ECMAScript 只支持实现继承,而且其实现继承主要是依靠原型链来实现的.所以,下面所要说的原型链继承.借用构造函数继承.组合继承.原型式继承.寄生式继承和寄生组合式继承都属于实现继承. 最后的最后,我会解释 ES6 中的 exten

  • 利用JS实现文字的聚合动画效果

    前言 所谓文字聚合效果,原理就是将容器分为若干的小块,然后每个小块设置 background-poisition ,最后添加css3动画就可以了,去掉注释,也就仅仅20行的代码. 先来看看效果图: js代码如下: //c为列数,r为行数,把box划分成多少个小块 var box = document.querySelector('.boxWrap1'),c=4,r=8; //每个小块的宽高 var w = box.offsetWidth/c,h = box.offsetHeight/r; //循

  • JavaScript常见继承模式实例小结

    本文实例总结了JavaScript常见继承模式.分享给大家供大家参考,具体如下: JavaScript中并没有传统的面向对象语言中的类的概念,但是却实现了特殊的继承机制. (阅读此文您首先需要知道原型的知识) 先来说说第一种继承方式,原型链继承. 一. 原型链继承 所谓原型链继承,就是让父类的一个实例作为子类的原型. 即 : parentInstance = new Parent(); child.prototype = parentInstance; 这样,在创建子类的实例时,子类实例的__p

  • JavaScript常见JSON操作实例分析

    本文实例讲述了JavaScript常见JSON操作.分享给大家供大家参考,具体如下: 1.JSON JSON,JavaScript Object Notation)(JavaScript对象表示法) ,是存储和交换文本信息的语法,是基于JavaScript的一个子集.数据格式简单,易于读写,占用带宽小,类似 XML,但JSON 比 XML更小.更快,更易解析. JSON不支持变量.函数.对象实例,只是一种数据的格式.数据在键值对中,数据由逗号分隔. 2.JSON数据类型 (1)简单数据类型:字符

  • JS常见构造模式实例对比分析

    本文实例分析了JS常见构造模式.分享给大家供大家参考,具体如下: 1.工厂模式 没有解决对象识别的问题.因为函数内部使用了new Object来创建对象 function Factory(name,age) { var o=new Object(); o.name=name; o.age=age; o.what=what;//用函数引用的方式消除重复创建相同函数的弊端,节省资源.函数引用可以修改this的指向,函数调用不可以! return o; } what=funciton() { aler

  • JavaScript对象创建模式实例汇总

    本文实例总结了JavaScript对象创建模式.分享给大家供大家参考,具体如下: 在JavaScript中创建对象是很容易的,可以使用对象字面量或者构造函数.常用的创建对象的模式有以下几种: 一. 工厂模式 工厂模式抽象了具体对象的过程,用函数来封装以特ing接口创建对象的细节. 如下: function createAnimal(name, age) { var o = new Object(); o.name = name; o.age = age; o.sayName = function

  • JavaScript中继承用法实例分析

    本文实例分析了JavaScript中继承的用法.分享给大家供大家参考.具体如下: // define the Person Class function Person() {} Person.prototype.walk = function(){ alert ('I am walking!'); }; Person.prototype.sayHello = function(){ alert ('hello'); }; // define the Student class function

  • JavaScript实现Iterator模式实例分析

    本文实例讲述了JavaScript实现Iterator模式的方法.分享给大家供大家参考.具体分析如下: 经常在网上看到有不少JS设计模式的示例.这里写一下JavaScript实现Iterator模式的方法,记录在此,仅作备忘: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd&quo

  • JavaScript常见事件处理程序实例总结

    本文实例总结了JavaScript常见事件处理程序.分享给大家供大家参考,具体如下: 事件指的是使用者或者浏览器自身执行的某种动作(比如点击事件).响应这些事件的函数就叫做事件处理程序(或者叫事件监听器).事件处理程序的名字以"on"为前缀,比如 click 事件的事件处理程序就是 onclick. 1 HTML 事件处理程序 如果某个元素支持某个事件,那么它都有一个与相应的事件处理程序同名的 HTML 属性,我们可以通过这个属性来指定 JS: <input type="

  • Javascript 中介者模式实例

    问题: 在页面上,用户会有很多的操作 ,每进行一次操作我们需要去展示操作的结果,在这里我们有了一个问题,我们应如何去展示不同的结果呢?在展示的过程中我们还要考虑当页面功能不断的增加与修改,结果展示也不断的增多,我们要用一个应对这些变化最好的方法解决问题. 解决方法: 在网盘开发中就遇到了这样的一个问题,经过几番的思考,决定了用如下的方式去解决这个问题,先看图: 从图中我们可以知道,会使用到一个中介专门去接收使用者的信息,再发送到相应的展示主题,这样我们就可以解决了展示方式不段改变的问题,如何去展

  • JavaScript代码复用模式实例分析

    任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量避免使用这些模式,因为或多或少有带来一些问题:第二排是推荐篇,指的是推荐大家使用的模式,一般不会有什么问题. 模式1:默认模式 代码复用大家常用的默认模式,往往是有问题的,该模式使用Parent()的构造函数创建一个对象,并且将该对象赋值给Child()的原型.我们看一下代码: 复制代码 代码如下: fu

  • JavaScript数组去重算法实例小结

    本文实例总结了JavaScript数组去重算法.分享给大家供大家参考,具体如下: 测试用例: arr = ["1",3,"1",1,4,5,1,"2",5,1,{"name":"li","age":20},2,4,3,{"name":"li","age":20},""]; 方法一:借助于临时数组与indexO

随机推荐