翻阅MDN文档,我发现了关于JS中对象属性继承的两个有趣的点。
关于 Object.defineProperty() 的文章指出
与访问器属性不同,数据属性始终在对象本身上设置,而不是在原型上设置。但是,如果继承了不可写的数据属性,仍然会阻止在对象上对其进行修改。
据我理解,这意味着对象的任何访问器属性或不可写数据属性都不能被其子对象覆盖,除非使用 Object.defineProperty() 静态方法在子对象上显式定义此类属性。
此外,super还有一个特殊的方面,它在当前
this
对象上设置一个属性,如下所示:
设置
的属性,例如super
,其行为类似于super.x = 1
。这是将 super 简单地理解为“原型对象的引用”的情况之一,因为它实际上将属性设置为Reflect.set(Object.getPrototypeOf(objectLiteral), "x", 1, this)
。this
但是使用
super
也不允许覆盖访问器或不可写数据属性,如下突出显示:
但是,
仍然参考原型对象的属性描述符,这意味着你不能重写不可写的属性,并且将调用setter。super.x = 1
考虑以下代码:
class A {
get ap() { return this.x;}
set ap(arg) { this.x = arg; }
}
Object.defineProperty(A.prototype, 'dp', {writable: true, value: 'writable'});
Object.defineProperty(A.prototype, 'nwdp', {writable: false, value: 'locked'});
class B extends A {
method() {
super.dp = 200; // Override it
super.ap = 'bird'; // Doesn't override it; Invokes setter, ap at A.prototype
super.nwdp = 'unlocked'; // Doesn't override it; Throws TypeError
}
}
b = new B()
当调用
b.method()
时,它会在实例上设置 dp
和 x
,b
(使用 Object.getOwnPropertyNames(b)
检查),但会抛出 TypeError
。
Uncaught TypeError: Cannot assign to read only property 'nwdp' of object '#B'
同样,当
super.prop = <any_value>
用于对象字面量时,它的功能相同,但不会抛出任何错误。
let obj1 = {
dp: 'writable',
get ap() { return this.x; },
set ap(arg) { this.x = arg; }
};
Object.defineProperty(obj1, 'nwdp', {writable: false, value: 'locked'});
let obj2 = {
method() {
super.dp = 200; // Overrides it
super.ap = 'bird'; // Doesn't override it; Invokes setter, ap at obj1
// Doesn't override it; But doesn't throw any kind of error as if it doesn't do anything
super.nwdp = 'unlocked';
}
};
Object.setPrototypeOf(obj2, obj1);
obj2.method();
使用
Object.getOwnPropertyNames(obj2)
显示对象 dp
上的属性 x
、method
和 obj2
。
所以,问题实际上是为什么 JS 会这样?
在这两种情况下,它的行为完全相同,除了当它涉及使用
super.prop = <any_value>
覆盖子对象上的不可写数据属性时,它会抛出 TypeError
来表示为 类实例 定义的属性,而它看起来它在对象文字的情况下忽略了相同的语句。
这是故意的吗?如果是这样,其背后的原因是什么?有记录在任何地方吗?
class
主体的所有部分都是“严格”模式代码,此时您会收到类型错误。你的第二个例子并不“严格”。如果你愿意的话,你可以这样做,然后你就会得到错误。