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 相关文章推荐
Jquery 实现Tab效果 思路是js思路
Mar 02 Javascript
jquery uaMatch源代码
Feb 14 Javascript
javascript数组快速打乱重排的方法
Jan 02 Javascript
javascript中日期函数new Date()的浏览器兼容性问题
Sep 05 Javascript
Bootstrap树形菜单插件TreeView.js使用方法详解
Nov 01 Javascript
纯js的右下角弹窗实例
Mar 12 Javascript
微信小程序中使用Promise进行异步流程处理的实例详解
Aug 17 Javascript
详解Vue 全局引入bass.scss 处理方案
Mar 26 Javascript
使用FormData实现上传多个文件
Dec 04 Javascript
vue2.x数组劫持原理的实现
Apr 19 Javascript
JavaScript undefined及null区别实例解析
Jul 21 Javascript
vue如何使用外部特殊字体的操作
Jul 30 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使用正则表达式获取图片url的方法
2015/01/16 PHP
分享php代码将360浏览器导出的favdb的sqlite数据库文件转换为html
2015/12/09 PHP
ThinkPHP的SAE开发相关注意事项详解
2016/10/09 PHP
php实现的简单数据库操作Model类
2016/11/16 PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
2020/05/02 PHP
如何在PHP中使用数组
2020/06/09 PHP
用js实现多域名不同文件的调用方法
2007/01/12 Javascript
一个很简单的jquery+xml+ajax的无刷新树结构(无css,后台是c#)
2010/06/02 Javascript
圣诞节Merry Christmas给博客添加浪漫的下雪效果基于jquery实现
2012/12/27 Javascript
根据json字符串生成Html的一种方式
2013/01/09 Javascript
jQuery 全选/反选以及单击行改变背景色实例
2013/07/02 Javascript
使用jquery选择器如何获取父级元素、同级元素、子元素
2014/05/14 Javascript
js实现同一个页面多个渐变效果的方法
2015/04/10 Javascript
jQuery实现自动与手动切换的滚动新闻特效代码分享
2015/08/27 Javascript
不得不分享的JavaScript常用方法函数集(下)
2015/12/25 Javascript
详解vue 中使用 AJAX获取数据的方法
2017/01/18 Javascript
JS随机排序数组实现方法分析
2017/10/11 Javascript
关于Vue源码vm.$watch()内部原理详解
2019/04/26 Javascript
基于JS实现数字动态变化显示效果附源码
2019/07/18 Javascript
关于layui表单中按钮自动提交的解决方法
2019/09/09 Javascript
JS禁用右键、禁用Ctrl+u、禁用Ctrl+s、禁用F12的实现代码
2020/12/01 Javascript
pytorch训练imagenet分类的方法
2018/07/27 Python
Python对称的二叉树多种思路实现方法
2020/02/28 Python
PyCharm 在Windows的有用快捷键详解
2020/04/07 Python
如何理解Python中的变量
2020/06/01 Python
详解Python的爬虫框架 Scrapy
2020/08/03 Python
兰蔻加拿大官方网站:Lancome加拿大
2016/08/05 全球购物
FC-Moto西班牙:摩托车手最大的购物场所之一
2019/04/11 全球购物
DOM和JQuery对象有什么区别
2016/11/11 面试题
深圳茁壮笔试题
2015/05/28 面试题
六一儿童节活动策划方案
2014/01/27 职场文书
晚会主持词开场白
2014/03/17 职场文书
2014年禁毒工作总结
2014/11/24 职场文书
2016中秋节月饼促销广告语
2016/01/28 职场文书
Android 界面一键变灰 深色主题工具类
2022/04/28 Java/Android
Windows10安装Apache2.4的方法步骤
2022/06/25 Servers