javascript中可靠实现只读原型常量需用object.defineproperty设置writable: false,或es6+类的static属性/访问器;直接赋值到prototype无法阻止修改,命名约定无保护作用。

在 JavaScript 中,构造函数原型上定义的属性默认是可变的,但可以通过 Object.defineProperty 配合 writable: false 和 enumerable: false(或 true)来模拟“常量”行为——即所有实例共享、不可被意外修改的只读属性。
使用 Object.defineProperty 定义只读原型属性
直接在原型上赋值(如 MyClass.prototype.MY_CONST = 42)无法阻止后续修改。真正可靠的方案是用 Object.defineProperty 显式设置 writable: false:
- 该属性会存在于构造函数的
prototype上,所有实例通过原型链访问 -
writable: false阻止重新赋值(严格模式下会报错,非严格模式下静默失败) -
configurable: false可选,防止属性被删除或重新定义(增强“常量”语义)
示例:
function Circle() {}
Object.defineProperty(Circle.prototype, 'PI', {
value: Math.PI,
writable: false,
enumerable: true, // 可选:设为 true 则 for...in 会遍历到
configurable: false
});
<p>const c1 = new Circle();
console.log(c1.PI); // 3.141592653589793
c1.PI = 3; // 无效(非严格模式静默失败;严格模式 TypeError)
</p>
更现代的写法:类 + static + get(推荐用于新项目)
如果你使用 ES6+ 类语法,更清晰的做法是将常量定义为 static 属性或 static get 访问器:
-
static PI = Math.PI:静态属性,直接通过类名访问(Circle.PI),不依赖实例 -
static get PI():只读访问器,同样不可赋值,且逻辑可延迟计算
注意:静态属性不会自动出现在实例原型链上,但可通过 this.constructor.PI 在实例方法中访问:
class Circle {
static PI = Math.PI;
getArea() {
return this.constructor.PI * this.radius ** 2;
}
}
为什么不推荐直接挂载到 prototype 上并命名为大写?
仅靠命名约定(如 MyClass.prototype.MAX_SIZE = 100)不能提供任何保护:
- 任何实例或外部代码都能改写它:
inst.MAX_SIZE = 200 - 没有运行时约束,容易引发隐蔽 bug
- 不符合“常量”的语义预期
补充:Symbol 作为唯一键也可用于隐藏常量
若希望常量不被枚举或意外覆盖,可用 Symbol 作属性键:
const PI_SYMBOL = Symbol('PI');
Object.defineProperty(Circle.prototype, PI_SYMBOL, {
value: Math.PI,
writable: false,
enumerable: false,
configurable: false
});
// 使用时需显式引用 Symbol:this[PI_SYMBOL]
这种方式更隐蔽安全,适合内部常量,但可读性和调试性略低。
本质上,JavaScript 没有原生的“只读原型常量”语法糖,可靠实现依赖 Object.defineProperty 或类的 static 成员。选择哪种方式取决于你的环境(是否支持 class)、是否需要实例可访问、以及对可枚举性/可见性的要求。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











