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规范。转换程序通常会牺牲一些边缘情况的正确性来换取运行时效率。
相关文章