详述JavaScript实现继承的几种方式(推荐)


Posted in Javascript onMarch 22, 2016

ECMAScript只支持实现继承,而且其实现继承主要是依靠原型链来实现的。

原型链

原型链的基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。每一个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针。如果:我们让原型对象A等于另一个类型B的实例,那么原型对象A就会有一个指针指向B的原型对象,相应的B的原型对象中保存着指向其构造函数的指针。假如B的原型对象又是另一个类型的实例,那么上述的关系依旧成立,如此层层递进,就构成了实例与原型的链条。

详述JavaScript实现继承的几种方式(推荐) 

实例以及构造函数和原型之间的关系图如下所示:

详述JavaScript实现继承的几种方式(推荐) 

person.constructor现在指向的是Parent,这是因为Child.prototype指向了Parent的原型,而Parent原型对象的constructor指向Parent。

当以读取模式访问一个实例属性时,首先会在实例中搜索该属性,如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现的集成中,搜索过程就会沿着原型链继续向上,直到搜索到原型链的末端。

例如,调用person.getParentValue()方法,1)搜索实例;2)搜索Child.prototype;3)搜索Parent.prototype;找到了getParentValue()方法停止。

1、默认的原型

前面的例子中展示的原型链少了一环,所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。因此默认的原型都包含一个内部指针,指向Object.prototype,这也正是所有自定义类型会继承toString()、ValueOf()等默认方法的根本原因。换句话说Object.prototype就是原型链的末端。

详述JavaScript实现继承的几种方式(推荐) 

2、确定原型和实例的关系

通过两种方式可以确定原型和实例之间的关系,第一种是使用instanceOf操作符,第二种是使用isPrototypeOf()方法。
实例 instanceOf 原型链 中出现过的构造函数,都会返回true

console.log(person instanceOf Child);//true 

console.log(person instanceOf Parent);//true 
console.log(person instanceOf Object);//true 
isPrototype(),只要是原型链中出现过的原型,都可以说是该原型链所派生出来的实例的原型,因此也返回true. 
console.log(Object.prototype.isPrototypeOf(instance));//true 
console.log(Parent.prototype.isPrototypeOf(instance));//true 
console.log(Child.prototype.isPrototypeOf(instance));//true

3、谨慎地定义方法

子类型有时候需要覆盖超类型中的某个方法,或者需要添加超类型中不存在的莫个方法,注意:给原型添加方法的代码一定要放在替换原型的语句之后。

当通过Child的实例调用getParentValue()时,调用的是这个重新定义过的方法,但是通过Parent的实例调用getParentValue()时,调用的还是原来的方法。

格外需要注意的是:必须要在Parent的实例替换原型之后,再定义这两个方法。

还有一点需要特别注意的是:通过原型链实现继承时,不能使用对象字面量创建原型方法,因为这样做会重写原型链。

详述JavaScript实现继承的几种方式(推荐) 

以上代码刚把Parent的实例赋值给Child的原型对象,紧接着又将原型替换成一个字面量,替换成字面量之后,Child原型实际上包含的是一个Object的实例,而不再是Parent的实例,因此我们设想中的原型链被切断.Parent和Child之间没有任何关联。

4、原型链的问题

原型链很强大,可以利用它来实现继承,但是也有一些问题,主要的问题还是包含引用类型值的原型属性会被所有实例共享。因此我们在构造函数中定义实例属性。但是在通过原型来实现继承时,原型对象其实变成了另一个类型的实例。于是原先定义在构造函数中的实例属性变成了原型属性了。

举例说明如下:

详述JavaScript实现继承的几种方式(推荐) 

在Parent构造函数中定义了一个friends属性,该属性值是一个数组(引用类型值)。这样,Parent的每个实例都会各自包含自己的friends属性。当Child通过原型链继承了Parent之后,Child.prototype也用用了friends属性——这就好像friends属性是定义在Child.prototype一样。这样Child的所有实例都会共享这个friends属性,因此我们对kid1.friends做的修改,在kid2.friends中也会体现出来,显然,这不是我们想要的。

原型链的另一个问题是:在创建子类型的实例时,不能在不影响所有对象实例的情况下,给超类型的构造函数传递参数。因此,我们通常很少会单独使用原型链。

借用构造函数

为了解决原型中包含引用类型值带来的一些问题,引入了借用构造函数的技术。这种技术的基础思想是:在子类型构造函数的内部调用超类型构造函数。

详述JavaScript实现继承的几种方式(推荐) 

Parent.call(this)在新创建的Child实例的环境下调用了Parent构造函数。在新创建的Child实例环境下调用Parent构造函数。这样,就在新的Child对象上,此处的kid1和kid2对象上执行Parent()函数中定义的对象初始化代码。这样,每个Child实例就都会具有自己的friends属性的副本了。

借用构造函数的方式可以在子类型的构造函数中向超类型构造函数传递参数。

详述JavaScript实现继承的几种方式(推荐) 

为了确保子类型的熟悉不会被父类的构造函数重写,可以在调用父类构造函数之后,再添加子类型的属性。
构造函数的问题:

构造函数模式的问题,在于方法都在构造函数中定义,函数复用无从谈起,因此,借用构造函数的模式也很少单独使用。

组合继承

组合继承指的是将原型链和借用构造函数的技术组合在一块,从而发挥二者之长。即:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。

详述JavaScript实现继承的几种方式(推荐) 

Person构造函数定义了两个属性:name和friends。Person的原型定义了一个方法sayName()。Child构造函数在调用Parent构造函数时,传入了name参数,紧接着又定义了自己的属性age。然后将Person的实例赋值给Child的原型,然后又在该原型上定义了方法sayAge().这样,两个不同的Child实例既分别拥有自己的属性,包括引用类型的属性,又可以使用相同的方法了。
组合继承避免了原型链和构造函数的缺陷,融合了他们的有点,成为JavaScript中最常用的继承模式。而且,instanceOf和isPropertyOf()也能够识别基于组合继承创建的对象。

最后,关于JS对象和继承都还有几种模式没有写,或者说,我自己也还未去深刻研究,但是,我想,首先将组合模式应用的游刃有余。并且,对于为何选用组合模式,知其然,知其所以然。

关于JavaScript实现继承的几种方式(推荐),小编就给大家介绍到这里,希望对大家有所帮助!

Javascript 相关文章推荐
在js中单选框和复选框获取值的方式
Nov 06 Javascript
js获取元素在浏览器中的绝对位置
Jul 24 Javascript
麻雀虽小五脏俱全 Dojo自定义控件应用
Sep 04 Javascript
Web Inspector:关于在 Sublime Text 中调试Js的介绍
Apr 18 Javascript
ANGULARJS中用NG-BIND指令实现单向绑定的例子
Dec 08 Javascript
详解jQuery中的元素的属性和相关操作
Aug 14 Javascript
jquery实现点击向下展开菜单项(伸缩导航)效果
Aug 22 Javascript
AngularJS使用ngMessages进行表单验证
Dec 27 Javascript
微信小程序 欢迎界面开发的实例详解
Nov 30 Javascript
详解Vue.js入门环境搭建
Mar 17 Javascript
详解微信小程序缓存--缓存时效性
May 02 Javascript
js实现简单掷骰子效果
Oct 24 Javascript
jQuery toggle 代替方法
Mar 22 #Javascript
一款简单的jQuery图片标注效果附源码下载
Mar 22 #Javascript
酷炫jQuery全屏3D焦点图动画效果
Mar 22 #Javascript
浅析C/C++,Java,PHP,JavaScript,Json数组、对象赋值时最后一个元素后面是否可以带逗号
Mar 22 #Javascript
使用struts2+Ajax+jquery验证用户名是否已被注册
Mar 22 #Javascript
使用getBoundingClientRect方法实现简洁的sticky组件的方法
Mar 22 #Javascript
Node.js文件操作方法汇总
Mar 22 #Javascript
You might like
德生H-501的评价与改造
2021/03/02 无线电
无数据库的详细域名查询程序PHP版(2)
2006/10/09 PHP
PHP的面试题集,附我的答案和分析(一)
2006/11/19 PHP
PHP 创建标签云函数代码
2010/05/26 PHP
php清除和销毁session的方法分析
2015/03/19 PHP
PHP+MySQL统计该库中每个表的记录数并按递减顺序排列的方法
2016/02/15 PHP
php Session无效分析资料整理
2016/11/29 PHP
替换php字符串中的单引号为双引号的方法
2017/02/16 PHP
PHP7使用ODBC连接SQL Server2008 R2数据库示例【基于thinkPHP5.1框架】
2019/05/06 PHP
php实现将数组或对象写入到文件的方法小结【三种方法】
2020/04/22 PHP
js利用div背景,做一个竖线的效果。
2008/11/22 Javascript
JavaScript 模拟用户单击事件
2009/12/31 Javascript
JQuery插入DOM节点的方法
2015/06/11 Javascript
jquery表单插件Autotab使用方法详解
2016/06/24 Javascript
jQuery EasyUI Draggable拖动组件
2017/03/01 Javascript
JavaScript 上传文件(psd,压缩包等),图片,视频的实现方法
2017/06/19 Javascript
three.js中3D视野的缩放实现代码
2017/11/16 Javascript
echarts整合多个类似option的方法实例
2018/07/10 Javascript
[01:03:51]2018DOTA2亚洲邀请赛 4.7 淘汰赛 VP vs LGD 第三场
2018/04/09 DOTA
python开发之基于thread线程搜索本地文件的方法
2015/11/11 Python
python PyTorch参数初始化和Finetune
2018/02/11 Python
python协程之动态添加任务的方法
2019/02/19 Python
11个Python Pandas小技巧让你的工作更高效(附代码实例)
2019/04/30 Python
Linux下远程连接Jupyter+pyspark部署教程
2019/06/21 Python
Python 3 判断2个字典相同
2019/08/06 Python
在Pytorch中使用样本权重(sample_weight)的正确方法
2019/08/17 Python
PyTorch预训练的实现
2019/09/18 Python
Python with语句和过程抽取思想
2019/12/23 Python
英国领先的餐饮折扣俱乐部:Gourmet Society
2020/07/26 全球购物
优秀共产党员先进事迹材料
2014/05/06 职场文书
欢迎标语大全
2014/06/21 职场文书
大学生求职信怎么写
2015/03/19 职场文书
单方投资意向书
2015/05/11 职场文书
运动员入场词
2015/07/18 职场文书
采购员工作总结范文
2015/08/12 职场文书
《风筝》教学反思
2016/02/23 职场文书