重新理解JavaScript的六种继承方式


Posted in Javascript onMarch 24, 2017

类式继承(构造函数)

JS中其实是没有类的概念的,所谓的类也是模拟出来的。特别是当我们是用new 关键字的时候,就使得“类”的概念就越像其他语言中的类了。类式继承是在函数对象内调用父类的构造函数,使得自身获得父类的方法和属性。call和apply方法为类式继承提供了支持。通过改变this的作用环境,使得子类本身具有父类的各种属性。

var father = function() {
this.age = 52;
this.say = function() {
alert('hello i am '+ this.name ' and i am '+this.age + 'years old');
}
}
var child = function() {
this.name = 'bill';
father.call(this);
}
var man = new child();
man.say();

 原型继承

原型继承在开发中经常用到。它有别于类继承是因为继承不在对象本身,而在对象的原型上(prototype)。每一个对象都有原型,在浏览器中它体现在一个隐藏的__proto__属性上。在一些现代浏览器中你可以更改它们。比如在zepto中,就是通过添加zepto的fn对象到一个空的数组的__proto__属性上去,从而使得该数组成为一个zepto对象并且拥有所有的方法。话说回来,当一个对象需要调用某个方法时,它回去最近的原型上查找该方法,如果没有找到,它会再次往下继续查找。这样逐级查找,一直找到了要找的方法。 这些查找的原型构成了该对象的原型链条。原型最后指向的是null。我们说的原型继承,就是将父对像的方法给子类的原型。子类的构造函数中不拥有这些方法和属性。

var father = function() {
}
father.prototype.a = function() {
}
var child = function(){}
//开始继承
child.prototype = new father();
var man = new child();
man.a();

可以看到第七行实现了原型继承。很多人并不陌生这种方式。通过在浏览器中打印man我们就可以查看各个原型的继承关系。

重新理解JavaScript的六种继承方式

可以看到逐级的关系child->object(father实例化的对象)->father。child是通过中间层继承了father的原型上的东西的。但是为什么中间还有一层object呢, 为什么不把child.prototype = father.prototype。 答案是如果这样做child和father就没有区别了。大家应该还记得在prototype中有个constructor属性,指向的是构造函数。按照正常的情况我们要把constructor的值改回来指向child的构造函数。但如果直接把father.prototype赋值给child.prototype,那么constructor应该指向谁呢?所以很显然只能通过中间层才能使得child和father保持为独立的对象。

类式继承和原型继承的对比

构造函数(类)式继承

首先,构造函数继承的方法都会存在父对象之中,每一次实例,都会将funciton保存在内存中,这样的做法毫无以为会带来性能上的问题。

其次,类式继承是不可变的。无法复用,在运行时,无法修改或者添加新的方法,这种方式是一种固步自封的死方法。实践中很少单纯使用。

原型继承

优点:

原型链可改变:原型链上的父类可替换可扩展

可以通过改变原型链接而对子类进行修改的。另外就是类式继承不支持多重继承,而对于原型继承来说,你只需要写好extend对对象进行扩展即可。

但是原型链继承也有2个问题。

第一,包含引用类型值的原型属性会被所有实例共享(可以这样理解:执行sub1.arr.push(2);先对sub1进行属性查找,找遍了实例属性(在本例中没有实例属性),没找到,就开始顺着原型链向上找,拿到了sub1的原型对象,一搜身,发现有arr属性。于是给arr末尾插入了2,所以sub2.arr也变了)。

第二,在创建子类型的实例时,不能向超类型的构造函数中传递参数。(实际上,应该说没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数)实践中很少单纯使用原型链。

function Super(){
this.val = 1;
this.arr = [1];
}
function Sub(){
// ...
}
Sub.prototype = new Super(); // 核心
var sub1 = new Sub();
var sub2 = new Sub();
sub1.val = 2;
sub1.arr.push(2);
alert(sub1.val); // 2
alert(sub2.val); // 1
alert(sub1.arr); // 1, 2
alert(sub2.arr); // 1, 2

总结:

类式继承在实例化时,父类可传参,不能复用(父类不可变,每一次实例都会将父类内容保存在内存中)

原型继承在实例化时,父类不可传参,可以复用(原型链可改变(父类可替换可扩展),父类不会保存在内存中,而是顺着原型链查找,但是结果是原型属性会被所有实例共享(尤其影响引用类型值))

组合继承(最常用)

组合继承将原型链和借用构造函数的技术结合到一起,发挥两者之长的一种继承模式。

思路是使用原型链实现对原型属性和方法的继承,通过借用构造函数实现对实例属性的继承。

function SuperType(name){
this.name = name;
this.numbers = [1,2,3];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function SubType(name,age){
SuperType.call(this,name);
this.age = age;
}
SubType.prototype = new SuperType();
SubType.prototype.sayAge = function(){
console.log(this.age);
}
var instance1 = new SubType('aaa',21);
instance1.numbers.push(666);
console.log(instance1.numbers);
instance1.sayName();
instance1.sayAge();
var instance2 = new SubType('bbb',22);
console.log(instance2.numbers);
instance2.sayName();
instance2.sayAge();

把实例函数都放在原型对象上,通过Sub.prototype = new Super();继承父类函数,以实现函数复用。

保留借用构造函数方式的优点,通过Super.call(this);继承父类的基本属性和引用属性,以实现传参;

优缺点

优点:

  1. 可传参
  2. 函数可复用
  3. 不存在引用属性共享问题(图纸)

缺点:

(一点小瑕疵)子类原型上有一份多余的父类实例属性,因为父类构造函数被调用了两次,生成了两份,而子类实例上的那一份屏蔽了子类原型上的。。。又是内存浪费,比刚才情况好点,不过确实是瑕疵。

以上所述是小编给大家介绍的JavaScript的六种继承方式,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
Javascript String对象扩展HTML编码和解码的方法
Jun 02 Javascript
Prototype源码浅析 String部分(一)之有关indexOf优化
Jan 15 Javascript
返回上一页并自动刷新的JavaScript代码
Feb 19 Javascript
js实现简单div拖拽功能实例
May 12 Javascript
ES6新特性之字符串的扩展实例分析
Apr 01 Javascript
Angular 2父子组件之间共享服务通信的实现
Jul 04 Javascript
移动端H5页面返回并刷新页面(BFcache)的方法
Nov 06 Javascript
JavaScript数据结构与算法之检索算法示例【二分查找法、计算重复次数】
Feb 22 Javascript
JS实现从对象获取对象中单个键值的方法示例
Jun 05 Javascript
微信小程序云开发获取文件夹下所有文件(推荐)
Nov 14 Javascript
基于Angular 8和Bootstrap 4实现动态主题切换的示例代码
Feb 11 Javascript
基于jsbarcode 生成条形码并将生成的条码保存至本地+源码
Apr 27 Javascript
JavaScript验证知识整理
Mar 24 #Javascript
Bootstrap常用组件学习(整理)
Mar 24 #Javascript
详解打造 Vue.js 可复用组件
Mar 24 #Javascript
JavaScript获取URL参数的方法之一
Mar 24 #Javascript
原生JS改变透明度实现轮播效果
Mar 24 #Javascript
深入理解vue路由的使用
Mar 24 #Javascript
原生JS实现导航下拉菜单效果
Nov 25 #Javascript
You might like
[原创]PHP中通过ADODB库实现调用Access数据库之修正版本
2006/12/31 PHP
php支付宝手机网页支付类实例
2015/03/04 PHP
PHP登录验证功能示例【用户名、密码、验证码、数据库、已登陆验证、自动登录和注销登录等】
2019/02/25 PHP
Windows服务器中PHP如何安装redis扩展
2019/09/27 PHP
PHP基于phpqrcode类生成二维码的方法示例详解
2020/08/07 PHP
怎么用javascript进行拖拽
2006/07/20 Javascript
javascript Prototype 对象扩展
2009/05/15 Javascript
JavaScript在IE和Firefox(火狐)的不兼容问题解决方法小结
2010/04/13 Javascript
javascript数组去掉重复
2011/05/12 Javascript
js 分页全选或反选标识实现代码
2011/08/09 Javascript
从阶乘函数对比Javascript和C#的异同
2012/05/31 Javascript
基于jQuery实现的百度导航li拖放排列效果,即时更新数据库
2012/07/31 Javascript
js限制文本框只能输入数字方法小结
2014/06/16 Javascript
JQuery插件iScroll实现下拉刷新,滚动翻页特效
2014/06/22 Javascript
JavaScript中判断整数的多种方法总结
2014/11/08 Javascript
浅谈js的setInterval事件
2014/12/05 Javascript
JavaScript实现获取dom中class的方法
2015/02/09 Javascript
JS中prototype的用法实例分析
2015/03/19 Javascript
js改变embed标签src值的方法
2015/04/10 Javascript
nodejs实现获取某宝商品分类
2015/05/28 NodeJs
简要了解jQuery移动web开发的响应式布局设计
2015/12/04 Javascript
AngularJS使用指令增强标准表单元素功能
2016/07/01 Javascript
JS提示:Uncaught SyntaxError: Unexpected token ILLEGAL错误的解决方法
2016/08/19 Javascript
微信小程序顶部可滚动导航效果
2017/10/31 Javascript
nodejs结合Socket.IO实现的即时通讯功能详解
2018/01/12 NodeJs
webpack中使用iconfont字体图标的方法
2018/02/22 Javascript
vue使用技巧及vue项目中遇到的问题
2018/06/04 Javascript
Python中elasticsearch插入和更新数据的实现方法
2018/04/01 Python
opencv改变imshow窗口大小,窗口位置的方法
2018/04/02 Python
在cmd命令行里进入和退出Python程序的方法
2018/05/12 Python
开业庆典答谢词
2014/01/18 职场文书
入党思想汇报怎么写
2014/04/03 职场文书
2014年销售人员工作总结
2014/11/27 职场文书
三八妇女节慰问信
2015/02/14 职场文书
民间借贷借条范本
2015/05/25 职场文书
源码解读Spring-Integration执行过程
2021/06/11 Java/Android