文以《JavaScript高级程序设计》上的内容为骨架,补充了ES6 Class的相关内容,从我认为更容易理解的角度将继承这件事叙述出来,希望大家能有所收获。

1. 继承分类


先来个整体印象。如图所示,JS中继承可以按照是否使用object函数(在下文中会提到),将继承分成两部分(Object.create是ES5新增的方法,用来规范化这个函数)。

其中,原型链继承和原型式继承有一样的优缺点,构造函数继承与寄生式继承也相互对应。寄生组合继承基于Object.create, 同时优化了组合继承,成为了完美的继承方式。ES6 Class Extends的结果与寄生组合继承基本一致,但是实现方案又略有不同。

下面马上进入正题:

2. 继承方式


2.1 原型式继承

核心:将父类的实例作为子类的原型。

  1. SubType.prototype = new SuperType()
  2. // 所有涉及到原型链继承的继承方式都要修改子类构造函数的指向,
  3. // 否则子类实例的构造函数会指向SuperType。
  4. SubType.prototype.constructor = SubType;

优点:父类方法可以复用。

缺点:

1、父类的引用属性会被所有子类实例共享

2、子类构建实例时不能向父类传递参数

2.2 构造函数继承

核心:将父类构造函数的内容复制给了子类的构造函数。这是所有继承中唯一一个不涉及到prototype的继承。

  1. SuperType.call(SubType);

优点:和原型链继承完全反过来

1、父类的引用属性不会被共享

2、子类构建实例时可以向父类传递参数

缺点:父类的方法不能复用,子类实例的方法每次都是单独创建的。

2.3 组合继承

核心:原型式继承和构造函数继承的组合,兼具了二者的优点。

  1. function SuperType() {
  2. this.name = 'parent';
  3. this.arr = [1, 2, 3];
  4. }
  5.  
  6. SuperType.prototype.say = function() {
  7. console.log('this is parent')
  8. }
  9.  
  10. function SubType() {
  11. SuperType.call(this) // 第二次调用SuperType
  12. }
  13.  
  14. SubType.prototype = new SuperType() // 第一次调用SuperType

优点:

1、父类的方法可以被复用

2、父类的引用属性不会被共享

3、子类构建实例时可以向父类传递参数

缺点:调用了两次父类的构造函数,第一次给子类的原型添加了父类的name, arr属性,第二次又给子类的构造函数添加了父类的name, arr属性,从而覆盖了子类原型中的同名参数。这种被覆盖的情况造成了性能上的浪费。

2.4 原型式继承

核心:原型式继承的object方法本质上是对参数对象的一个浅复制。

优点:父类方法可以复用。

缺点:

1、父类的引用属性会被所有子类实例共享

2、子类构建实例时不能向父类传递参数

  1. function object(o){
  2. function F(){}
  3. F.prototype = o;
  4. return new F();
  5. }
  6.  
  7. var person = {
  8. name: "Nicholas",
  9. friends: ["Shelby", "Court", "Van"]
  10. };
  11.  
  12. var anotherPerson = object(person);
  13. anotherPerson.name = "Greg";
  14. anotherPerson.friends.push("Rob");
  15.  
  16. var yetAnotherPerson = object(person);
  17. yetAnotherPerson.name = "Linda";
  18. yetAnotherPerson.friends.push("Barbie");
  19. alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"

ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一 个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象。在传入一个参数的情况下, Object.create()与 object()方法的行为相同。——《JAVASCript高级编程》

所以上文中代码可以转变为:

  1. var yetAnotherPerson = object(person); =>
  2. var yetAnotherPerson = Object.create(person);

2.5 寄生式继承

核心:使用原型式继承获得一个目标对象的浅复制,然后增强这个浅复制的能力。

优缺点:仅提供一种思路,没什么优点。

  1. function createAnother(original){
  2. var clone=object(original); //通过调用函数创建一个新对象
  3. clone.sayHi = function(){ //以某种方式来增强这个对象
  4. alert("hi");
  5. };
  6. return clone; //返回这个对象
  7. }
  8.  
  9. var person = {
  10. name: "Nicholas",
  11. friends: ["Shelby", "Court", "Van"]
  12. };
  13.  
  14. var anotherPerson = createAnother(person);
  15. anotherPerson.sayHi(); //"hi"

2.6 寄生组合继承

刚才说到组合继承有一个会两次调用父类的构造函数造成浪费的缺点,寄生组合继承就可以解决这个问题。

  1. function inheritPrototype(subType, superType){
  2. var prototype = object(superType.prototype); // 创建了父类原型的浅复制
  3. prototype.constructor = subType; // 修正原型的构造函数
  4. subType.prototype = prototype; // 将子类的原型替换为这个原型
  5. }
  6.  
  7. function SuperType(name){
  8. this.name = name;
  9. this.colors = ["red", "blue", "green"];
  10. }
  11.  
  12. SuperType.prototype.sayName = function(){
  13. alert(this.name);
  14. };
  15.  
  16. function SubType(name, age){
  17. SuperType.call(this, name);
  18. this.age = age;
  19. }
  20. // 核心:因为是对父类原型的复制,所以不包含父类的构造函数,
  21. // 也就不会调用两次父类的构造函数造成浪费
  22. inheritPrototype(SubType, SuperType);
  23. SubType.prototype.sayAge = function(){
  24. alert(this.age);
  25. }

  

优缺点:这是一种完美的继承方式。

2.7 ES6 Class extends

核心: ES6继承的结果和寄生组合继承相似,本质上,ES6继承是一种语法糖。但是,寄生组合继承是先创建子类实例this对象,然后再对其增强;而ES6先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。

  1. class A {}
  2.  
  3. class B extends A {
  4. constructor() {
  5. super();
  6. }
  7. }

ES6实现继承的具体原理:

  1. class A {
  2. }
  3.  
  4. class B {
  5. }
  6.  
  7. Object.setPrototypeOf = function (obj, proto) {
  8. obj.__proto__ = proto;
  9. return obj;
  10. }
  11.  
  12. // B 的实例继承 A 的实例
  13. Object.setPrototypeOf(B.prototype, A.prototype);
  14.  
  15. // B 继承 A 的静态属性
  16. Object.setPrototypeOf(B, A);

ES6继承与ES5继承的异同:

相同点:本质上ES6继承是ES5继承的语法糖。

不同点:

1、ES6继承中子类的构造函数的原型链指向父类的构造函数,ES5中使用的是构造函数复制,没有原型链指向。

2、ES6子类实例的构建,基于父类实例,ES5中不是。

3. 总结


  • 1、ES6 Class extends是ES5继承的语法糖
  • 2、JS的继承除了构造函数继承之外都基于原型链构建的
  • 3、可以用寄生组合继承实现ES6 Class extends,但是还是会有细微的差别

参考文章:

  • 1、《js继承、构造函数继承、原型链继承、组合继承、组合继承优化、寄生组合继承》
  • 2、《JavaScript高级编程》

 

javascript中继承方式及优缺点(三)的更多相关文章

  1. javascript中继承方式及优缺点(二)

    一.原型链继承 方式1: 原型链继承 (1)流程: ​ 1.定义父类型构造函数. ​ 2.给父类型的原型添加方法. ​ 3.定义子类型的构造函数. ​ 4.创建父类型的对象赋值给子类型的原型. ​ 5 ...

  2. javascript中继承方式及优缺点(一)

    分别介绍原型链继承.call/apply继承(借用构造函数继承).组合继承.原型式继承.寄生式继承.寄生组合式继承 1. 原型链继承 核心:将父类的实例作为子类的原型 function SuperTy ...

  3. JavaScript各种继承方式和优缺点

    好久没写博客啦,嘻嘻,这个月是2017年的最后一个月啦,大家应该都开始忙着写年终总结了吧,嘻嘻,小颖今天给大家分享下Javascript中的几种继承方式以及他们的优缺点. 1.借助构造函数实现继承 原 ...

  4. 谈谈JavaScript中继承方式

    聊一聊js中的继承 一.简单继承---使用原型赋值的方式继承,将实例化的对象,赋值给子级的原型 父级构造函数 function Parent(param) { this.name = 'parent' ...

  5. js各种继承方式和优缺点的介绍

    js各种继承方式和优缺点的介绍 作者: default 参考网址2 写在前面 本文讲解JavaScript各种继承方式和优缺点. 注意: 跟<JavaScript深入之创建对象>一样,更像 ...

  6. Javascript中继承

    Javascript中继承 构造函数继承 原型继承 call和apply继承 组合继承

  7. javascript中各种继承方式的优缺点

    javascript中实现继承的方式有很多种,一般都是通过原型链和构造函数来实现.下面对各种实现方式进行分析,总结各自的优缺点. 一 原型继承 let Super = functioin(name = ...

  8. 实现JavaScript中继承的三种方式

    在JavaScript中,继承可以通过三种手法实现原型链继承 使用apply.call方法 对象实例间的继承.     一.原型链继承 在原型链继承方面,JavaScript与java.c#等语言类似 ...

  9. js的三种继承方式及其优缺点

    [转] 第一种,prototype的方式: //父类 function person(){ this.hair = 'black'; this.eye = 'black'; this.skin = ' ...

随机推荐

  1. 【转帖】超能课堂(188) WiFi 6凭什么可以如此“六”?

    https://www.expreview.com/69155.html 不明觉厉 这些东西 自己理解的还是少呢 电脑硬件可能一年甚至不到一年就会开始更新换代,但是路由器就不一样,它们的更新换代往往是 ...

  2. 小菜鸟之java异常

    一.异常简介 什么是异常? 异常就是有异于常态,和正常情况不一样,有错误出错.在java中,阻止当前方法或作用域的情况,称之为异常. java中异常的体系是怎么样的呢? 1.Java中的所有不正常类都 ...

  3. 升级降级(期望DP)2019 Multi-University Training Contest 7 hdu杭电多校第7场(Kejin Player)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6656 题意: 有 1~n 个等级,你现在是1级,求升到n级的花费期望.会给你n个条件(i~i+1级升级 ...

  4. GitHub从小白到熟悉<一>

    注册开始

  5. python协程gevent案例:爬取斗鱼美女图片

    分析 分析网站寻找需要的网址 用谷歌浏览器摁F12打开开发者工具,然后打开斗鱼颜值分类的页面,如图: 在里面的请求中,最后发现它是以ajax加载的数据,数据格式为json,如图: 圈住的部分是我们需要 ...

  6. ProGuard 最全混淆规则说明

    Input/Output Options 输入输出选项 -include filename 递归引入目录的配置文件 -basedirectory directoryname -injars class ...

  7. Git 一般性操作

    git全局设定 git config --global user.name “码云账号” git config --global user.email “码云注册邮箱” git 定位文件夹cd进入到需 ...

  8. 【Activiti】为每一个流程绑定相应的业务对象的2种方法

    方式1: 在保存每一个流程实例时,设置多个流程变量,通过多个流程变量的组合来过滤筛选符合该组合条件的流程实例,以后在需要查询对应业务对象所对应的流程实例时,只需查询包含该流程变量的值的流程实例即可. ...

  9. echarts属性的设置

    // 全图默认背景  // backgroundColor: ‘rgba(0,0,0,0)’, // 默认色板 color: ['#ff7f50','#87cefa','#da70d6','#32cd ...

  10. JQ向上取整 和向下取整 四舍五入

    向上取整   var a = 23.2325236   var abc = Math.ceil(a); //注意:Math.ceil(a)不要单独写一行,否则向上取整失败   abc = 24;   ...