在构造函数原型上定义常量属性必须用 object.defineproperty 设置 writable: false 和 configurable: false,直接赋值或 object.freeze 均无法保证不可修改;若为对象常量,需先冻结再挂载。

在构造函数原型上定义“常量属性”需要特别注意:JavaScript 中没有真正的“只读原型属性”,但可以通过约定和机制实现语义上的常量效果。关键在于——原型上的属性默认可被修改,必须主动设防。
构造函数原型上定义常量属性的正确做法
- 原型(
Constructor.prototype)是所有实例共享的,因此定义在上面的值天然具有“全局一份”的特性 - 但
prototype本身是一个普通对象,其属性默认可读写,不能靠const保证不可变 - 所以“常量”需通过
Object.defineProperty配合writable: false和configurable: false实现真正意义上的不可改
例如,为 Person 构造函数定义一个不可修改的原型常量 MAX_AGE:
function Person(name, age) {
this.name = name;
this.age = age;
}
// 在 prototype 上定义只读常量属性
Object.defineProperty(Person.prototype, 'MAX_AGE', {
value: 150,
writable: false, // 禁止重新赋值
enumerable: true, // 可被 for...in 遍历(按需设)
configurable: false // 禁止 delete 或重新 define
});
// 使用
const p = new Person('Alice', 30);
console.log(p.MAX_AGE); // 150
p.MAX_AGE = 200; // 静默失败(非严格模式)或报错(严格模式)
为什么不能用 prototype.MAX_AGE = 150?
- 这样赋值会创建一个可写、可配置、可枚举的普通属性
- 任何代码都可以
Person.prototype.MAX_AGE = 999覆盖它,破坏常量语义 - 也不受
Object.freeze(Person.prototype)全局冻结保护(因为freeze不冻结已有可配置属性的writable)
更稳妥的补充策略
-
若常量是对象(如配置项),需先
Object.freeze()再挂到原型上:Object.defineProperty(Person.prototype, 'DEFAULT_OPTS', { value: Object.freeze({ theme: 'light', lang: 'zh' }), writable: false, configurable: false, enumerable: true }); 避免在原型上定义可变引用类型常量(如数组、普通对象),除非已冻结
不推荐用
const声明后直接挂载(const MAX = 150; Person.prototype.MAX = MAX),这仅防止变量重赋值,不阻止原型属性被改
实际开发中的常见误区
- 把
constructor里的this.xxx = VAL当作常量 → 这是每个实例的副本,不是共享常量 - 误以为
Object.freeze(Person.prototype)能一劳永逸 → 它只能冻结原型对象自身,对已有属性的writable无影响 - 用
static(类语法)混淆原型场景 →class的static属于类本身,不属于原型链,与prototype无关
不复杂但容易忽略。











