原稿:语雀 · class A extends B{}过程 · 原目录:JavaScript › JS继承 › class A extends B{}过程

工具准备:Babel

一直只是不断看到大家说extends过程是语法糖,背后还是原型链继承,所以其中到底封装了什么?

首先祭出神器Babel,它可以把ES6+的JS代码转为向后兼容的旧版本实现

准备好下面的ES6类继承代码,在babel网站上转换

class B{   constructor(name){     this.name = name;   }   foo(){}};class A extends B{    constructor(name,age){     super(name)     this.age = age;   }};let a = new A('Jim',18);console.log(a)//{name:'Jim',age:18}

ES6 转 ES5 结果分析

转换后的内容非常长,看几个重要部分

首先是A、B的声明部分

image.png

对于原型方法来说,es6 使用 _createClass() 完成继承,该方法直接通过 defineProperty() 为构造函数的原型上增加属性

image.png

A 的声明首先是一个立即执行函数,其中 _inherits() 是最重要的一步,解释了为什么说ES6 继承依旧基于“寄生式组合继承”,因为该函数和寄生式组合中的 inheritPrototype(subType, superType) 作用基本一致:创建父类原型副本,修改为子类原型,同时更新子类 constructor 指向

image.png

_inherits()

此外,_inherits() 最后会执行  _setPrototypeOf(subClass, superClass),让子类的隐式原型指向父类,形成第二条继承链;接下来执行 _createSuper(),通过 getPrototypeOf() 获取父类构造函数并返回保存在 _super(),该立即执行函数返回一个子类构造函数

image.png

创建子类对象时,调用上述返回的子类构造函数 _A(),相当于 new _A():通过 _super.call(),父类上的属性被继承


小结

通过分析 Babel 转换结果可以确定,ES6 继承基于 ES5 的“寄生式组合继承”,除了原型链上的继承外,子类(构造函数)本身继承了父类(构造函数)

extends 在实现继承方面,本质上也是原型链继承,不过相对于 es5 原有继承模式而言,多了一条原型链继承,以父类 B,子类 A 说明 2. 父类的原型方法和静态方法会通过 _createClass 调用 defineproperty() 添加到父类原型与父类自身上 4. 子类继承会首先在立即执行函数里调用 _inherits(A, _B) ,实现两步原型链继承

  • A.prototype = Object.create(B.prototype, {constructor:{value: A}})
  • Object.setPrototypeOf(A, B)

立即执行函数返回子类的构造函数 2. 创建子类时,在子类构造函数里进行初始化操作

  • 首先调用 _classCallCheck 来阻止直接函数调用
  • 调用 _possibleConstructorReturn(this, _getPrototypeOf(A).apply(this, args)) ,相当于调用了父类构造函数,所以上面第二处原型链 A.__proto__ =B 就得到了体现
  • 最后调用 _createClass 为子类原型添加原型方法或静态方法
image.png

参考链接: