You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何JS类实例重写不可写属性抛TypeError,对象字面量无报错?

前言

查阅MDN文档时,我发现了JavaScript中对象属性继承的两个有趣要点。Object.defineProperty()的文档指出:

与访问器属性不同,数据属性始终设置在对象本身而非原型上。但如果继承了不可写的数据属性,仍无法在对象上修改该属性。

我的理解是,对象的任何访问器属性或不可写数据属性,除非子对象通过Object.defineProperty()静态方法显式定义该属性,否则无法被子对象重写。

此外,super还有一个特殊特性:它会在当前this对象上设置属性,相关描述如下:

设置super的属性(如super.x = 1)的行为类似于Reflect.set(Object.getPrototypeOf(objectLiteral), "x", 1, this)。这是仅将super理解为"原型对象引用"不够准确的情况之一,因为它实际上是在this上设置属性。

但使用super同样不允许重写访问器或不可写数据属性,相关说明如下:

不过,super.x = 1仍会检查原型对象的属性描述符,这意味着你无法重写不可写属性,且会调用对应的setter。


问题

考虑以下代码:

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; // 重写该属性
        super.ap = 'bird';  // 不重写;调用A.prototype上的ap setter
        super.nwdp = 'unlocked';  // 不重写;抛出TypeError
    }
}

b = new B()

调用b.method()时,会在实例b上设置dp和x(可通过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; // 重写该属性
        super.ap = 'bird'; // 不重写;调用obj1上的ap setter

        // 不重写;但无任何报错,仿佛该语句未执行
        super.nwdp = 'unlocked';
        }
};
Object.setPrototypeOf(obj2, obj1);
obj2.method();

通过Object.getOwnPropertyNames(obj2)可看到,对象obj2上存在dp、x和method属性。

因此,核心问题是:JavaScript为何会有这种行为?
两种场景下的行为基本一致,唯独在使用super.prop = <any_value>重写子对象的不可写数据属性时,类实例会抛出TypeError,而对象字面量则似乎忽略该语句。

这种差异是有意设计的吗?背后的原因是什么?是否有相关文档记录?


解答

这种差异是ECMAScript规范的有意设计,核心原因在于严格模式的默认开启状态不同:

  1. 类的方法默认运行在严格模式下
    类的所有内部方法(包括实例方法、静态方法等)默认启用严格模式。当在严格模式下执行super.nwdp = 'unlocked'时,Reflect.set操作会因为原型上的属性不可写而返回false,严格模式会将这个失败的赋值操作抛出TypeError。

  2. 对象字面量的方法默认运行在非严格模式下
    对象字面量中定义的方法默认处于非严格模式。此时Reflect.set操作失败返回false,但非严格模式下不会抛出错误,只是静默忽略该赋值操作。

如果给对象字面量的方法加上严格模式,行为就会和类的场景完全一致:

let obj2 = {
    method() {
        'use strict';
        super.dp = 200;
        super.ap = 'bird';
        super.nwdp = 'unlocked'; // 此时会抛出TypeError
    }
};
Object.setPrototypeOf(obj2, obj1);
obj2.method();

规范中的核心逻辑是:super赋值操作本质上调用Reflect.set(prototype, prop, value, this),当该方法返回false时,若当前处于严格模式,则必须抛出TypeError;非严格模式下则直接忽略失败。类的方法强制开启严格模式,而对象字面量方法默认不开启,这就导致了两者的行为差异。


内容的提问来源于stack exchange,提问作者Perspicacious

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 16:05:40