在ES5继承的实现非常有趣的,由于没有传统面向对象类的概念,Javascript利用原型链的特性来实现继承,这其中有很多的属性指向和需要注意的地方。

原型链的特点和实现已经在之前的一篇整理说过了,就是通过将子类构造函数的原型作为父类构造函数的实例,这样就连通了子类-子类原型-父类,原型链的特点就是逐层查找,从子类开始一直往上直到所有对象的原型Object.prototype,找到属性方法之后就会停止查找,所以下层的属性方法会覆盖上层。

一个基本的基于原型链的继承过程大概是这样的:

//先来个父类,带些属性
function Super(){ this.flag = true; } //为了提高复用性,方法绑定在父类原型属性上 Super.prototype.getFlag = function(){ return this.flag; } //来个子类 function Sub(){ this.subFlag = false; } //实现继承 Sub.prototype = new Super; //给子类添加子类特有的方法,注意顺序要在继承之后 Sub.prototype.getSubFlag = function(){ return this.subFlag; } //构造实例 var es5 = new Sub;

原型链实现的继承主要有几个问题:
1、本来我们为了构造函数属性的封装私有性,方法的复用性,提倡将属性声明在构造函数内,而将方法绑定在原型对象上,但是现在子类的原型是父类的一个实例,自然父类的属性就变成子类原型的属性了;
这就会带来一个问题,我们知道构造函数的原型属性在所有构造的实例中是共享的,所以原型中属性的改变会反应到所有的实例上,这就违背了我们想要属性私有化的初衷;
2、创建子类的实例时,不能向父类的构造函数传递参数

function Super(){ this.flag = true; } function Sub(){ this.subFlag = false; } Sub.prototype = new Super; var obj = new Sub(); obj.flag = flase; //修改之后,由于是原型上的属性,之后创建的所有实例都会受到影响 var obj_2 = new Sub(); console.log(obj.flag) //false;

为了解决以上两个问题,有一个叫借用构造函数的方法
只需要在子类构造函数内部使用apply或者call来调用父类的函数即可在实现属性继承的同时,又能传递参数,又能让实例不互相影响

function Super(){ this.flag = true; } function Sub(){ Super.call(this) //如果父类可以需要接收参数,这里也可以直接传递 } var obj = new Sub(); obj.flag = flase; var obj_2 = new Sub(); console.log(obj.flag) //依然是true,不会相互影响

结合借用构造函数和原型链的方法,可以实现比较完美的继承方法,可以称为组合继承:

function Super(){ this.flag = true; } Super.prototype.getFlag = function(){ return this.flag; //继承方法 } function Sub(){ this.subFlag = flase Super.call(this) //继承属性 } Sub.prototype = new Super; var obj = new Sub(); Super.prototype.getSubFlag = function(){ return this.flag; }

这里还有个小问题,Sub.prototype = new Super; 会导致Sub.prototype的constructor指向Super;
然而constructor的定义是要指向原型属性对应的构造函数的,Sub.prototype是Sub构造函数的原型,所以应该添加一句纠正:
Sub.prototype.constructor = Sub;

看完ES5的实现,再来看看ES6的继承实现方法,其内部其实也是ES5组合继承的方式,通过call借用构造函数,在A类构造函数中调用相关属性,再用原型链的连接实现方法的继承

class B extends A { constructor() { return A.call(this); //继承属性 } } A.prototype = new B; //继承方法

ES6封装了class,extends关键字来实现继承,内部的实现原理其实依然是基于上面所讲的原型链,不过进过一层封装后,Javascript的继承得以更加简洁优雅地实现

class ColorPoint extends Point { constructor(x, y, color) { super(x, y); // 等同于parent.constructor(x, y) this.color = color; } toString() { return this.color + ' ' + super.toString(); // 等同于parent.toString() } }

通过constructor来定义构造函数,用super调用父类的属性方法

ES6中Class充当了ES5中,构造函数在继承实现过程中的作用
同样有原型属性prototype,以及在ES5中用来指向构造函数原型的__proto__属性,这个属性在ES6中的指向有一些主动的修改。
一个继承语句同时存在两条继承链:一条实现属性继承,一条实现方法继承。

class A extends B {} A.__proto__ === B; //继承属性 A.prototype.__proto__ === B.prototype; //继承方法

ES6的子类的__proto__是父类,子类的原型的__proto__是父类的原型
第二条继承链理解起来没有什么问题,对应到ES5中的A.prototype = new B;A.prototype作为B构造的实例,指向构造函数B的原型B.prototype,

但是在ES5中A.__proto__是指向Function.prototype的,因为每一个构造函数其实都是Function这个对象构造的,ES6中子类的__proto__指向父类可以实现属性的继承,在ES5中在没有用借用继承的时候由于父类属性被子类原型继承,所有的子类实例实际上都是同一个属性引用。

在ES6中实现了子类继承父类属性,在构造实例的时候会直接拿到子类的属性,不需要查找到原型属性上面去,ES6新的静态方法和静态属性(只能在构造函数上访问)也是通过这样类的直接继承来实现,至于普通复用方法还是放到原型链上,道理和实现和ES5是一样的。

此外我认为这里修改A.__proto__的指向是有意区分ES6中继承和实例化,同时建立子类和父类直接的关系,ES5的子类的构造函数通过子类的原型与父类的构造函数连接,不存在直接的关系;

可以这么说,在ES5继承和构造实例,ES6构造实例的时候可以理解__proto__原型指针是用来指向构造函数的原型的,但是在ES6继承中,__proto__指继承自哪个类或原型,在A继承B之后,构造一个实例 var obj = new A; 会发现它所有的属性指向都是和ES5一致的。

有个有趣的地方:ES6继承是在父类创建this对象,在子类constructor中来修饰父类的this,ES5是在子类创建this,将父类的属性方法绑定到子类,由于原生的构造函数(Function,Array等)没有this,子类无法通过call/apply(this)获得其内部属性,所以在ES5无法继承,ES6实现后可以为原生构造函数封装一些有趣的接口,比方说阮一峰老师的这个给Array实例封装一个版本记录和回滚的方法:

class VersionedArray extends Array { constructor() { super(); this.history = [[]]; } commit() { this.history.push(this.slice()); } revert() { this.splice(0, this.length, ...this.history[this.history.length - 1]); } } var x = new VersionedArray(); x.push(1); x.push(2); x // [1, 2] x.history // [[]] x.commit(); x.history // [[], [1, 2]] x.push(3); x // [1, 2, 3] x.revert(); x // [1, 2]

最后做一个ES5和ES6的继承小结:
ES5最经典的继承方法是用组合继承的方式,原型链继承方法,借用函数继承属性,ES6也是基于这样的方式,但是封装了更优雅简洁的api,让Javascript越来越强大,修改了一些属性指向,规范了继承的操作,区分开了继承实现和实例构造,此外ES6继承还能实现更多的继承需求和场景。

文/Daguo(简书作者)
原文链接:http://www.jianshu.com/p/342966fdf816

ES5和ES6中对于继承的实现方法相关推荐

  1. ES5与ES6中如何实现继承

    ES5中实现继承 // 使用类或不使用类继承 // 继承 (2个类连起来) function Animal (lengthNumber) {this.lengthNumber = legsNumber ...

  2. typescript学习之路(四) —— ts类的继承(包含es5以及es6的类继承)

    上一文已经写了es5,es6等类的定义,所以本章主要写es5和es6的继承,由于es6的继承和ts的继承如出一辙,只是加了类型定义而已,所以ts的继承稍微写下,不会太详细. 文章目录 es5继承 原型 ...

  3. ES5和ES6中的变量声明提升

    ES5和ES6中的变量声明提升 Example1: a=2; var a; console.log( a ); //结果为2 Example2: console.log( a ); //结果是unde ...

  4. ES5和ES6中的this指向

    ES5和ES6中的this指向 ES5: 1.在普通函数中,this指向的是函数调用者,默认情况下,this指向的是window 2.在严格模式下,如果没有直接调用者,在函数中,this的值为unde ...

  5. JavaScript-----静态成员在ES5和ES6中的实现

    在学习静态成员在ES5和ES6中的实现之前我们先来了解一下静态成员的定义.  静态成员 :类的所有对象共享的成员(类的成员变量(属性)和成员方法) 看完这个定义我相信大家还是不太了解静态成员是什么意思 ...

  6. 使对象具有ES6中Iterator接口的实现方法

    es6中只有具有iterator接口的数组或者类数组(arguments)都可以使用for of来循环,但是对于对象来说不行,可以利用迭代器中的原理来给对象生成一个迭代器,实现让对象可以使用for o ...

  7. ES5、ES6 如何实现继承

    完整高频题库仓库地址:https://github.com/hzfe/awesome-interview 完整高频题库阅读地址:https://febook.hzfe.org/ 相关问题 关于 ES5 ...

  8. es5与es6中如何处理不确定参数?以及es6中rest parameter的强大之处

    本人学习过程中编写,定有不足之处,如果有错误,请您积极指正:如果有帮助,请不要吝啬您的赞美(点赞),欢迎各位大佬点赞评论. es5中处理不确定参数(arguments) function sum(){ ...

  9. es6中的静态属性和方法

    es6 中 class 带static 关键字是静态方法 静态方法和对象无关,外部对象变量不能调用静态方法和属性 外部可以通过类名来调用 对象原型(就是类下面的方法)方法没有办法调用对象上面的静态方法 ...

最新文章

  1. 计算机php外文翻译,php外文翻译.doc
  2. Java实现算法导论中Rabin-Karp字符串匹配算法
  3. wxWidgets:wxBitmapButton类用法
  4. 特斯拉标准续航版Model Y为什么下架?马斯克这么回答
  5. 代写python assignment经常用到的代码语法分享!
  6. springboot集成购买阿里的rocketmq
  7. mac 爱普生打印机驱动_爱普生l301打印机驱动Mac版-爱普生L301驱动Mac版下载 V9.31-PC6苹果网...
  8. Tomcat8正确配置环境变量详细方法
  9. 产品经理面试,说一下你是怎么做产品规划的?
  10. 四位共阳极数码管显示函数_求各位大神指正,四位一体共阳极数码管数字钟程序,仿真能运行,实物就只显8个8,不动...
  11. Hazelcast是什么
  12. python 图灵完备_区块链学习6:图灵完备和图灵不完备
  13. IT痴汉的工作现状12-叶志坚离职了
  14. ibmx3400M3服务器装系统教程,免费安装系统!IBM x3400 M3不足1万2
  15. 天通手机卫星电话、天通卫星系统终端、SRRC、型号核准、CTA、进网许可
  16. 一文搞清楚软链接和硬链接
  17. Opencv-Python提取掌纹图片ROI
  18. 武邑中学2021高考成绩查询,武邑中学2019高考喜报成绩、清华北大本一上线人数情况...
  19. mac修改服务器身份,Mac 基础教程:如何打开身份不明开发者的应用程序
  20. 电脑每次启动都检测硬盘

热门文章

  1. select/poll原理(阻塞挂起) 正常(阻塞轮询)
  2. 消息队列—简介以及使用场景
  3. c++学习笔记之基础篇
  4. .net IDE 界面编程AutoScale属性惹的大麻烦
  5. Django框架(三)—— orm增删改查、Django生命周期
  6. project5 大数据
  7. 团队作业5——测试与发布(Alpha版本)
  8. [deviceone开发]-毛玻璃效果示例
  9. 2010502260926_《avolon》
  10. Flex里监听mouseDownOutside事件解决弹出窗口点击空白关闭功能