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 相关文章推荐
Prototype 学习 工具函数学习($w,$F方法)
Jul 12 Javascript
jQuery中:image选择器用法实例
Jan 03 Javascript
jQuery垂直多级导航菜单代码分享
Aug 18 Javascript
深入学习JavaScript的AngularJS框架中指令的使用方法
Mar 05 Javascript
JS作为值的函数用法示例
Jun 20 Javascript
JS常用倒计时代码实例总结
Feb 07 Javascript
vue项目关闭eslint校验
Mar 21 Javascript
解决vue中使用Axios调用接口时出现的ie数据处理问题
Aug 13 Javascript
浅谈js中的bind
Mar 18 Javascript
微信小程序录音实现功能并上传(使用node解析接收)
Feb 26 Javascript
解决iView Table组件宽度只变大不变小的问题
Nov 13 Javascript
Ajax实现页面无刷新留言效果
Mar 24 Javascript
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
基于PHP与XML的PDF文档生成技术
2006/10/09 PHP
php读取txt文件组成SQL并插入数据库的代码(原创自Zjmainstay)
2012/07/31 PHP
smarty模板引擎中自定义函数的方法
2015/01/22 PHP
PHP实现微信JS-SDK接口选择相册及拍照并上传的方法
2016/12/05 PHP
Yii2实现多域名跨域同步登录退出
2017/02/04 PHP
THINKPHP5.1 Config的配置与获取详解
2020/06/08 PHP
js中prototype用法详细介绍
2013/11/14 Javascript
基于jquery扩展漂亮的下拉框可以二次修改
2013/11/19 Javascript
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
jquery图片滚动放大代码分享(2)
2015/08/28 Javascript
js立即执行函数: (function ( ){})( ) 与 (function ( ){}( )) 有什么区别?
2015/11/18 Javascript
AngularJS页面访问时出现页面闪烁问题的解决
2016/03/06 Javascript
JS验证码实现代码
2017/09/14 Javascript
jQuery获取所有父级元素及同级元素及子元素的方法(推荐)
2018/01/21 jQuery
微信小程序模板template简单用法示例
2018/12/04 Javascript
vue实现节点增删改功能
2019/09/26 Javascript
python33 urllib2使用方法细节讲解
2013/12/03 Python
python中遍历文件的3个方法
2014/09/02 Python
python执行外部程序的常用方法小结
2015/03/21 Python
Python装饰器使用实例:验证参数合法性
2015/06/24 Python
用pandas按列合并两个文件的实例
2018/04/12 Python
django DRF图片路径问题的解决方法
2018/09/10 Python
python opencv 批量改变图片的尺寸大小的方法
2019/06/28 Python
Flask框架学习笔记之路由和反向路由详解【图文与实例】
2019/08/12 Python
Python 用turtle实现用正方形画圆的例子
2019/11/21 Python
基于python3抓取pinpoint应用信息入库
2020/01/08 Python
意大利自行车商店:Cingolani Bike Shop
2019/09/03 全球购物
就业推荐表自我鉴定范文
2014/03/21 职场文书
好书伴我成长演讲稿
2014/05/14 职场文书
“四风”问题的主要表现和危害思想汇报
2014/09/19 职场文书
党员批评与自我批评(5篇)
2014/09/23 职场文书
授权收款委托书
2014/09/23 职场文书
餐厅保洁员岗位职责
2015/04/10 职场文书
2019年XX公司的晨会制度及流程!
2019/07/23 职场文书
Python 中random 库的详细使用
2021/06/03 Python
php修改word的实例方法
2021/11/17 PHP