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 相关文章推荐
js no-repeat写法 背景不重复
Mar 18 Javascript
菜鸟学习JavaScript小实验之函数引用
Nov 17 Javascript
关于javascript中的typeof和instanceof介绍
Dec 04 Javascript
String.prototype实现的一些javascript函数介绍
Nov 22 Javascript
关于Javascript 对象(object)的prototype
May 09 Javascript
js中数组结合字符串实现查找(屏蔽广告判断url等)
Mar 30 Javascript
Ext JS框架程序中阻止键盘触发回退或者刷新页面的代码分享
Jun 07 Javascript
JS中对Cookie的操作详解
Aug 05 Javascript
javascript replace()第二个参数为函数时的参数用法
Dec 26 Javascript
Vue中引入样式文件的方法
Aug 18 Javascript
react 国际化的实现代码示例
Sep 14 Javascript
使用Promise封装小程序wx.request的实现方法
Nov 13 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
长波有什么东西
2021/03/01 无线电
Php 构造函数construct的前下划线是双的_
2009/12/08 PHP
php简单开启gzip压缩方法(zlib.output_compression)
2013/04/13 PHP
超级强大的表单验证
2006/06/26 Javascript
根据分辨率不同,调用不同的css文件
2006/08/25 Javascript
jquery 新手学习常见问题解决方法
2010/04/18 Javascript
javascript动画浅析
2012/08/30 Javascript
JQuery操作Select的Options的Bug(IE8兼容性视图模式)
2013/04/21 Javascript
js/jquery解析json和数组格式的方法详解
2014/01/09 Javascript
在javascript中如何得到中英文混合字符串的长度
2014/01/17 Javascript
js实现用户注册协议倒计时的方法
2015/01/21 Javascript
Bootstrap3 多选和单选框(checkbox)
2016/12/29 Javascript
javascript修改浏览器title方法 JS动态修改浏览器标题
2017/11/30 Javascript
深入解析ES6中的promise
2018/11/08 Javascript
微信小程序登录按钮遮罩浮层效果的实现方法
2018/12/16 Javascript
JavaScript实现的九种排序算法
2019/03/04 Javascript
JS数组方法reverse()用法实例分析
2020/01/18 Javascript
微信小程序单选框自定义赋值
2020/05/26 Javascript
python通过openpyxl生成Excel文件的方法
2015/05/12 Python
python八大排序算法速度实例对比
2017/12/06 Python
python爬取m3u8连接的视频
2018/02/28 Python
python 执行文件时额外参数获取的实例
2018/12/18 Python
Python从list类型、range()序列简单认识类(class)【可迭代】
2019/05/31 Python
python实现简单井字棋小游戏
2020/03/05 Python
python 实现PIL模块在图片画线写字
2020/05/16 Python
使用AJAX和Django获取数据的方法实例
2020/10/25 Python
PAUL HEWITT手表美国站:德国北部时尚生活配饰品牌,船锚元素
2017/11/18 全球购物
助学贷款贫困证明
2014/09/23 职场文书
党的群众路线教育实践活动个人整改方案
2014/10/25 职场文书
华清池导游词
2015/02/02 职场文书
高中班主任培训心得体会
2016/01/07 职场文书
靠谱的活动总结
2019/04/16 职场文书
golang switch语句的灵活写法介绍
2021/05/06 Golang
深入理解Vue的数据响应式
2021/05/15 Vue.js
MYSQL 无法识别中文的永久解决方法
2021/06/03 MySQL
Go遍历struct,map,slice的实现
2021/06/13 Golang