抛出问题
下述代码中,Child
类继承自 Parent
类,我们希望 Child
类中 af
方法能够覆盖 Parent
类中箭头函数 af
方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Parent { af = () => { console.log('parent af') } } class Child extends Parent { af () { console.log('child af') } } const child = new Child() child.af()
|
上述代码调用 child.af()
后会打印出 ‘parent af’ 而非期望的 ‘child af’。为什么?
ES6 class -> ES5 function
1 2 3 4 5
| class Parent() { f() {console.log('parent f')} af = () => {console.log('parent af')} }
|
上述代码通过 babel
转义后变为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var Parent = function () { function Parent() { _classCallCheck(this, Parent); this.af = function () { console.log('parent af'); }; } _createClass(Parent, [{ key: 'f', value: function f() {console.log('parent f')} }]); return Parent; }()
|
从上述代码可以看出,f
通过 _createClass
绑定到 Parent
的 prototype
上,而箭头函数 af
则是直接绑定到 Parent
的 this
上,因此上述代码等价于:
1 2 3 4 5 6 7 8 9
| var Parent = function() { this.af = function () { console.log('parent af'); }; } Parent.prototype.f = function() { console.log('parent f') }
|
寄生组合式继承
回顾下经典的寄生组合式继承,通过 inherit
继承的类其原型链为:
child._proto_ => Child.prototype
Child.prototype._proto_ => F.prototype = Parent.prototype
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const createObj = obj => { function F(){} F.prototype = obj return new F() } const inherit = (Child, Parent) => { const p = createObj(Parent.prototype) p.constructor = Child Child.prototype = p } class Parent {} class Child {} inherit(Child, Parent) var child = new Child()
|
ES6 class extends 继承
事实上,ES6
中的 class extends
继承方式原理参照的就是寄生组合式继承,将文章最开始的 Child
类转义成 ES5
就变成了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| var Child = function (Parent) { _inherits(Child, Parent); function Child() { _classCallCheck(this, Child); return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments)); } _createClass(Child, [{ key: 'af', value: function af() {} }]); return Child; }(Parent);
|
上述代码等价于:
1 2 3 4 5
| var Child = function(){} Child.prototype.af = function() {console.log('child af')} inherit(Child, Parent)
|
根据继承原理,Child
会继承 Parent
的 this
属性,因此实例 child
内部含有从
Parent
中继承下来的 this.af
方法,会屏蔽 Child.prototype.af
方法,所以文章最开头的代码 child.af()
会打印出 parent af
而不是 child af
。
解决方案
如果父类中定义了箭头函数属性,子类中的同名函数欲覆盖它,则子类中同名函数也应该定义为箭头函数。
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
| class Child extends Parent { af = () => { console.log('child af') } } var Child = function (_Parent) { _inherits(Child, _Parent); function Child() { var _ref; var _temp, _this, _ret; _classCallCheck(this, Child); for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return _ret = (_temp = (_this = _possibleConstructorReturn(this, (_ref = Child.__proto__ || Object.getPrototypeOf(Child)).call.apply(_ref, [this].concat(args))), _this), _this.af = function () { console.log('child af'); }, _temp), _possibleConstructorReturn(_this, _ret); } return Child; }(Parent);
|
另外由于箭头函数 af
是直接绑定在父类函数上的,因此子类中无法通过 super.af()
的方式调用他,因为 super === Child.prototype
。