最近一直在看原型继承相关的东西,翻到这么一篇文章: 从ES6中的extends讲js原型链与继承javascript
文中有一个点让我很感兴趣,箭头函数在继承过程当中没法经过super关键字获取,这是为何呢?html
The super keyword is used to access and call functions on an object's parent - in MDN
大概有这么几个关键点:java
个人见解是不须要。
网上有些文章(好比这篇)写道:es6
由于若不执行super,则this没法初始化。
个人我的理解是,this是指代执行上下文环境的,不存在没法初始化的状况。更准确的说法是这样:若是不使用super方法,那么父类中的属性值没法进行初始化,若是这个时候子类经过this字段来访问了父类中的属性值,那么只能获得一个undefined。至于为何这么写编译的时候会报错?个人理解是,这应该是一种语法错误,并且是一种规范要求,ES6语法的规范要求,这种要求并非说会影响到代码的实际执行。举个栗子:typescript
// typescript中一段简单的继承代码实现 class Parent { name = 'parent'; func = function() { console.log('func in parent called.'); } } class Child extends Parent { age = 3; func = function() { console.log('age is: ', this.age); // 使用了this,不会报错 } }
这段代码很是简单,在子类中使用了this关键字,编译时不会报错,也能够正常执行。而后咱们进行一点修改,在子类中引入constructor方法segmentfault
class Child extends Parent { age = 3; // error TS2377: Constructors for derived classes must contain a 'super' call. constructor() { } func = function() { console.log('age is: ', this.age); } }
能够看到,编译阶段已经开始报错了。在typescript的语法中,子类的constructor方法中不但须要调用super方法,并且必须在第一行代码就调用super,不然都是会报错的。看下面这段代码:app
class Child extends Parent { age = 3; constructor() { console.log('First line in constructor without super method'); super(); // error TS2376: A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties. } func = function() { console.log('age is: ', this.age); } }
来,咱们接着改函数
class Parent { name = 'parent'; func = function() { console.log('func in parent called.'); } } class Child extends Parent { age = 3; constructor() { console.log('Show property of parent, name is: ', this.name); // error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. console.log('Show property of child, age is: ', this.age); // error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. super(); // error TS2376: A 'super' call must be the first statement in the constructor when a class contains initialized properties or has parameter properties. console.log('Show property of parent, name is: ', this.name); console.log('Show property of child, age is: ', this.age); } func = function() { console.log('age is: ', this.age); } }
能够看到,编译期已经开始报各类错误了,不过这不重要,咱们这里利用typescript的编译器(tsc)来进行编译,并查看编译后的代码内容:this
var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var Parent = (function () { function Parent() { this.name = 'parent'; this.func = function () { console.log('func in parent called.'); }; } return Parent; }()); var Child = (function (_super) { __extends(Child, _super); function Child() { var _this = this; _this.age = 3; _this.func = function () { console.log('age is: ', this.age); }; console.log('Show property of parent, name is: ', _this.name); // 输出undefined,由于此时子类的实例上尚未继承到父类的属性值 console.log('Show property of child, age is: ', _this.age); // 输出3,子类实例本身的属性值能够访问 _this = _super.call(this) || this; // 构造函数式的继承实现,这一步就是讲父类的属性值设置到子类实例上 console.log('Show property of parent, name is: ', _this.name); // 输出parent,此时子类的实例上通过上一步的继承,获得了父类的属性值 console.log('Show property of child, age is: ', _this.age); // 输出3,子类实例本身的属性值能够访问 return _this; } return Child; }(Parent)); //# sourceMappingURL=demo.js.map
由此能够知道,在ES6中使用extends进行继承操做的过程当中,prototype
咱们直接来看代码吧,关键点都注释了的
class Parent { public name = 'parent'; public static staticName = 'staticParent'; public static staticFunc() { console.log('staticFunc called in parent.'); } public arrowFunc = () => { console.log('arrowFunc called in parent.'); } public normalFunc() { console.log('normalFunc called in parent.') } } class Child extends Parent { public static staticFunc() { super.staticFunc(); console.log('staticFunc called in Child.'); } arrowFunc = () => { super.arrowFunc(); console.log('arrowFunc called in Child.'); } normalFunc() { super.normalFunc(); console.log('normalFunc called in Child.') } getName() { console.log('parent name is: ', super.name); console.log('parent staticName is: ', super.staticName); console.log('child name is: ', this.name); } } /** 编译后的代码 **/ var __extends = (this && this.__extends) || (function () { var extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var Parent = (function () { function Parent() { this.name = 'parent'; this.arrowFunc = function () { console.log('arrowFunc called in parent.'); }; } // 编译后的静态方法能够存在于Parent类的内部 Parent.staticFunc = function () { console.log('staticFunc called in parent.'); }; Parent.prototype.normalFunc = function () { console.log('normalFunc called in parent.'); }; return Parent; }()); Parent.staticName = 'staticParent'; // 编译后的静态属性依然存在于Parent类外 var Child = (function (_super) { __extends(Child, _super); function Child() { var _this = _super !== null && _super.apply(this, arguments) || this; _this.arrowFunc = function () { // 子类实例调用arrowFunc的时候会报错,由于_super.prototype上是不存在arrowFunc方法的 _super.prototype.arrowFunc.call(_this); // Uncaught TypeError: Cannot read property 'call' of undefined console.log('arrowFunc called in Child.'); }; return _this; } Child.staticFunc = function () { _super.staticFunc.call(this); // super能够正常访问父类的静态方法 console.log('staticFunc called in Child.'); }; Child.prototype.normalFunc = function () { _super.prototype.normalFunc.call(this); console.log('normalFunc called in Child.'); }; Child.prototype.getName = function () { console.log('parent name is: ', _super.prototype.name); // 输出undefined, 父类原型(_super.prototype)上不存在name属性 console.log('parent staticName is: ', _super.prototype.staticName); // 输出undefined,super没法正常访问父类的静态属性 console.log('child name is: ', this.name); // 输出parent,这是子类实例上的属性,继承自父类 }; return Child; }(Parent)); //# sourceMappingURL=demo.js.map
这里再顺嘴提一句,关于静态属性和静态方法的区别。为何在子类中经过super关键字来获取父类的静态方法通过编译后是_super.staticFunc,而获取静态属性依然是_super.prototype.staticName,从原型上获取致使获取失败呢?这个问题目前我尚未找到答案,但愿有知道的小伙伴能够不吝指教。
不过我却是搜到一些其余相关内容。
Class 的静态属性和实例属性
由于 ES6 明确规定,Class 内部只有静态方法,没有静态属性。
虽然这种规定从ES7开始获得了修正,咱们目前已经能够将静态属性写在Class的内部,可是通过编译以后能够发现,静态属性依然存在于类的实现的外部。
var Parent = (function () { function Parent() { this.name = 'parent'; this.arrowFunc = function () { console.log('arrowFunc called in parent.'); }; } // 编译后的静态方法能够存在于Parent类的内部 Parent.staticFunc = function () { console.log('staticFunc called in parent.'); }; Parent.prototype.normalFunc = function () { console.log('normalFunc called in parent.'); }; return Parent; }()); Parent.staticName = 'staticParent'; // 编译后的静态属性依然存在于Parent类外
问:箭头函数在继承过程当中没法经过super关键字获取,这是为何呢?
答:由于子类中使用super.prop和super[expr]的方式获取的是父类原型(prototype)上的方法,静态方法除外。
从ES6中的extends讲js原型链与继承
React ES6 class constructor super()
Class 的静态属性和实例属性