原稿:语雀 · 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的声明部分

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

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

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

创建子类对象时,调用上述返回的子类构造函数 _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 为子类原型添加原型方法或静态方法

参考链接: