ES6继承:使用`super`访问父类的属性
Javascript的super关键字,当我在Chrome、Babel、TypeScript上运行代码时,得到不同的结果。
我的问题是哪个结果是正确的?规范的哪个部分定义了这种行为?
以下代码:
class Point {
getX() {
console.log(this.x); // C
}
}
class ColorPoint extends Point {
constructor() {
super();
this.x = 2;
super.x = 3;
console.log(this.x) // A
console.log(super.x) // B
}
m() {
this.getX()
}
}
const cp = new ColorPoint();
cp.m();
结果:
- Chrome 58.0.3029.110 64位(V8 5.8.283.38)
- 巴别塔代表6.24.2
- 打字稿2.3
链接:
- gist
- babel
解决方案
简明答案:
Chrome是正确的。这是由于get和set之间的不平衡造成的。
OrdinarySet是reciever敏感的,但OrdinaryGet不敏感。
sosuper.x = 3具有与this.x = 3相同的效果,因为这里的接收方是this。评估永远不会达到this的super.x将始终得到undefined,因为A.prototype没有这样的字段。
更多详细信息:
super.x是SuperReference。而对SuperReference的赋值将调用PutValue(V, W),进而调用super对象的内部槽[[Set]],最后调用OrdinarySet。
在纯JavaScript中,super.x = 3语句基本等同于:
OrdinarySet(proto, 'x', 3, this)。
其中proto是超级对象,内部是构造函数ColorPoint的[[HomeObject]]。proto等同于Object.create(Point.prototype),如ClassDefinitionEvaluation所指定,作为[[HomeObject]]传递给构造函数。
现在让我们看看
OrdinarySet是如何工作的。在步骤4c和4d中,规范要求在接收方this而不是proto对象上执行设置操作。
是否将ExistingDescriptor设置为?接收方。[GetOwnProperty]。
如果ExistingDescriptor不是未定义的,则
如果IsAccessorDescriptor(ExistingDescriptor)为true,则返回false。
如果ExistingDescriptor.[[Writable]]为False,则返回False。
让valueDesc为PropertyDescriptor{[[value]]:v}。
返回?接收方.[[DefineOwnProperty]](P,valueDesc).
这些语句表示OrdinarySet(proto, 3, this)表示this.x = 3。
另一方面,OrdinaryGet忽略Receiver。super.x是
OrdinaryGet(proto, 'x', this)。
OrdinaryGet子句中根本没有Receiver!所以super.x等同于Object.create(Point.prototype).x,当然就是undefined。
经验法则是,如果代码转换程序和浏览器之间存在差异,浏览器(尤其是Chrome)通常更忠于ECMAScript规范。转换程序通常会牺牲一些边缘情况的正确性来换取运行时效率。
相关文章