JavaScript常见继承模式实例小结


Posted in Javascript onJanuary 11, 2019

本文实例总结了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程序设计有所帮助。

Javascript 相关文章推荐
javascript 字符串连接的性能问题(多浏览器)
Nov 18 Javascript
复制Input内容的js代码_支持所有浏览器,修正了Firefox3.5以上的问题
Jun 21 Javascript
javascript去掉前后空格的实例
Nov 07 Javascript
node.js中的buffer.fill方法使用说明
Dec 14 Javascript
深入理解JavaScript系列(43):设计模式之状态模式详解
Mar 04 Javascript
javascript生成大小写字母
Jul 03 Javascript
VC调用javascript的几种方法(推荐)
Aug 09 Javascript
jquery实现tab选项卡切换效果(悬停、下方横线动画位移)
May 05 jQuery
浅谈关于angularJs中使用$.ajax的注意点
Aug 12 Javascript
vue使用监听实现全选反选功能
Jul 06 Javascript
LayUI数据接口返回实体封装的例子
Sep 12 Javascript
Jquery ajax书写方法代码实例解析
Jun 12 jQuery
JavaScript惰性求值的一种实现方法示例
Jan 11 #Javascript
JavaScript创建对象的四种常用模式实例分析
Jan 11 #Javascript
详解Vue项目部署遇到的问题及解决方案
Jan 11 #Javascript
VeeValidate 的使用场景以及配置详解
Jan 11 #Javascript
JS函数节流和防抖之间的区分和实现详解
Jan 11 #Javascript
微信公众号H5支付接口调用方法
Jan 10 #Javascript
详解在Node.js中发起HTTP请求的5种方法
Jan 10 #Javascript
You might like
一个取得文件扩展名的函数
2006/10/09 PHP
json的键名为数字时的调用方式(示例代码)
2013/11/15 PHP
列举PHP的Yii 2框架的开发优势
2015/07/03 PHP
thinkPHP5 tablib标签库自定义方法详解
2017/05/10 PHP
PHP微信开发之微信录音临时转永久存储
2018/01/26 PHP
js获取变量
2006/08/24 Javascript
checkbox全选/取消全选以及checkbox遍历jQuery实现代码
2009/12/02 Javascript
解读JavaScript中 For, While与递归的用法
2013/05/07 Javascript
深入理解JavaScript系列(37):设计模式之享元模式详解
2015/03/04 Javascript
基于javascript制作微博发布栏效果
2016/04/04 Javascript
javascript字符串对象常用api函数小结(连接,替换,分割,转换等)
2016/09/20 Javascript
IONIC自定义subheader的最佳解决方案
2016/09/22 Javascript
javascript中this用法实例详解
2017/04/06 Javascript
vue实现微信分享朋友圈,发送朋友的示例讲解
2018/02/10 Javascript
centos 上快速搭建ghost博客方法分享
2018/05/23 Javascript
详解小程序不同页面之间通讯的解决方案
2018/11/23 Javascript
JavaScript数组、json对象、eval()函数用法实例分析
2019/02/21 Javascript
JavaScript实现图片轮播特效
2019/10/23 Javascript
Python使用functools实现注解同步方法
2018/02/06 Python
使用Python和xlwt向Excel文件中写入中文的实例
2018/04/21 Python
通过Pandas读取大文件的实例
2018/06/07 Python
Python爬取12306车次信息代码详解
2020/08/12 Python
Python json解析库jsonpath原理及使用示例
2020/11/25 Python
如何用python批量调整视频声音
2020/12/22 Python
CSS3实现类似翻书效果的过渡动画的示例代码
2019/09/06 HTML / CSS
前端canvas水印快速制作(附完整代码)
2019/09/19 HTML / CSS
小狗电器官方商城:中国高端吸尘器品牌
2017/03/29 全球购物
Linux Interview Questions For software testers
2012/06/02 面试题
大学生专科毕业生自我评价
2013/11/17 职场文书
数控个人求职信范文
2014/02/03 职场文书
不忘国耻振兴中华演讲稿
2014/05/14 职场文书
信访维稳工作汇报
2014/10/27 职场文书
2015年资料员工作总结
2015/04/25 职场文书
比赛主持人开场白
2015/05/29 职场文书
仅用几行Python代码就能复制她的U盘文件?
2021/06/26 Python
js不常见操作运算符总结
2021/11/20 Javascript