简介 在 ES5 中,实现继承的方式有多种,详细可以参考前面的文章JavaScript 之继承的多种方式和优缺点 ,这里主要说说 ES6 中 Class 的继承。
Class 可以通过 extends 关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Parent { constructor (name ) { this .name = name; } } class Child extends Parent { constructor (name, age ) { super (name); this .age = age; } } var child1 = new Child("kevin" , "18" );console .log(child1);
值得注意的是:
super 关键字表示父类的构造函数,相当于 ES5 的 Parent.call(this)。 子类必须在 constructor 方法中调用 super 方法,否则新建实例时会报错。这是因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法,子类就得不到 this 对象。 也正是因为这个原因,在子类的构造函数中,只有调用 super 之后,才可以使用 this 关键字,否则会报错。
类的 prototype 属性和proto 属性 在 ES5 实现之中,每一个对象都有__proto__属性,指向对应的构造函数的 prototype 属性。在 ES6 中,父类的静态方法,可以被子类继承,Class 作为构造函数的语法糖,同时有 prototype 属性和__proto__属性,因此同时存在两条继承链。
子类的__proto__属性,表示构造函数的继承,总是指向父类。
子类 prototype 属性的__proto__属性,表示方法的继承,总是指向父类的 prototype 属性。
1 2 3 4 5 6 7 8 9 10 11 class Foo { static classMethod ( ) { return "hello" ; } } class Bar extends Foo {}Bar.classMethod(); console .log(Child.__proto__ === Parent); console .log(Child.prototype.__proto__ === Parent.prototype);
ES6 的原型链示意图为:
我们会发现,相比寄生组合式继承,ES6 的 class 多了一个 Object.setPrototypeOf(Child, Parent) 的步骤。
Object.setPrototypeOf 等同于:
1 2 3 4 Object .setPrototypeOf = function (obj, proto ) { obj.__proto__ = proto; return obj; };
Object.getPrototypeOf() Object.getPrototypeOf 方法可以用来从子类上获取父类。
1 2 Object .getPrototypeOf(Child) === Parent;
因此,可以使用这个方法判断,一个类是否继承了另一个类。
Babel 编译 先看这段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class Parent { constructor (name ) { this .name = name; } } class Child extends Parent { constructor (name, age ) { super (name); this .age = age; } } var child1 = new Child("kevin" , "18" );console .log(child1);
Babel 编译 后为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 "use strict" ;function _typeof (obj ) { "@babel/helpers - typeof" ; if (typeof Symbol === "function" && typeof Symbol .iterator === "symbol" ) { _typeof = function _typeof (obj ) { return typeof obj; }; } else { _typeof = function _typeof (obj ) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol .prototype ? "symbol" : typeof obj; }; } return _typeof(obj); } function _inherits (subClass, superClass ) { if (typeof superClass !== "function" && superClass !== null ) { throw new TypeError ("Super expression must either be null or a function" ); } subClass.prototype = Object .create(superClass && superClass.prototype, { constructor : { value : subClass, writable : true , configurable : true }, }); if (superClass) _setPrototypeOf(subClass, superClass); } function _setPrototypeOf (o, p ) { _setPrototypeOf = Object .setPrototypeOf || function _setPrototypeOf (o, p ) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } function _createSuper (Derived ) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal ( ) { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this ).constructor; result = Reflect .construct(Super, arguments , NewTarget); } else { result = Super.apply(this , arguments ); } return _possibleConstructorReturn(this , result); }; } function _possibleConstructorReturn (self, call ) { if (call && (_typeof(call) === "object" || typeof call === "function" )) { return call; } else if (call !== void 0 ) { throw new TypeError ( "Derived constructors may only return object or undefined" ); } return _assertThisInitialized(self); } function _assertThisInitialized (self ) { if (self === void 0 ) { throw new ReferenceError ( "this hasn't been initialised - super() hasn't been called" ); } return self; } function _isNativeReflectConstruct ( ) { if (typeof Reflect === "undefined" || !Reflect .construct) return false ; if (Reflect .construct.sham) return false ; if (typeof Proxy === "function" ) return true ; try { Boolean .prototype.valueOf.call( Reflect .construct(Boolean , [], function ( ) {}) ); return true ; } catch (e) { return false ; } } function _getPrototypeOf (o ) { _getPrototypeOf = Object .setPrototypeOf ? Object .getPrototypeOf : function _getPrototypeOf (o ) { return o.__proto__ || Object .getPrototypeOf(o); }; return _getPrototypeOf(o); } function _instanceof (left, right ) { if ( right != null && typeof Symbol !== "undefined" && right[Symbol .hasInstance] ) { return !!right[Symbol .hasInstance](left); } else { return left instanceof right; } } function _classCallCheck (instance, Constructor ) { if (!_instanceof(instance, Constructor)) { throw new TypeError ("Cannot call a class as a function" ); } } var Parent = function Parent (name ) { _classCallCheck(this , Parent); this .name = name; }; var Child = (function (_Parent ) { _inherits(Child, _Parent); var _super = _createSuper(Child); function Child (name, age ) { var _this; _classCallCheck(this , Child); _this = _super.call(this , name); _this.age = age; return _this; } return Child; })(Parent); var child1 = new Child("kevin" , "18" );console .log(child1);
_inherits 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function _inherits (subClass, superClass ) { if (typeof superClass !== "function" && superClass !== null ) { throw new TypeError ("Super expression must either be null or a function" ); } subClass.prototype = Object .create(superClass && superClass.prototype, { constructor : { value : subClass, writable : true , configurable : true , }, }); if (superClass) _setPrototypeOf(subClass, superClass); }
_createSuper 函数里返回一个_possibleConstructorReturn 方法:
1 2 3 4 5 _createSuper(Child); _possibleConstructorReturn(this , Parent.call(this , name)); _this = _possibleConstructorReturn(this , Parent.call(this , name));
_possibleConstructorReturn 的源码为:
1 2 3 4 5 6 7 8 9 10 function _possibleConstructorReturn (self, call ) { if (call && (_typeof(call) === "object" || typeof call === "function" )) { return call; } else if (call !== void 0 ) { throw new TypeError ( "Derived constructors may only return object or undefined" ); } return _assertThisInitialized(self); }
在这里我们判断 Parent.call(this, name) 的返回值的类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Parent { constructor ( ) { this .xxx = xxx; } } class Parent { constructor ( ) { return { name : "kevin" , }; } } class Parent { constructor ( ) { return null ; } }
所以对于 Parent.call(this) 的值,如果是 object 类型或者是 function 类型,就返回 Parent.call(this),如果是 null 或者基本类型的值或者是 undefined,都会返回 self 也就是子类的 this。
总结 最后总体看下如何实现继承: 首先执行 _inherits(Child, Parent),建立 Child 和 Parent 的原型链关系,即 Object.setPrototypeOf(Child.prototype, Parent.prototype) 和 Object.setPrototypeOf(Child, Parent)。
然后调用 Parent.call(this, name),根据 Parent 构造函数的返回值类型确定子类构造函数 this 的初始值 _this。
最终,根据子类构造函数,修改 _this 的值,然后返回该值。
参考文献