javascript中原型对象不可变需组合手段实现:先用object.defineproperty设置writable:false、configurable:false、enumerable:false锁定属性,再用object.freeze()冻结对象内容,必要时配合getter统一管控访问逻辑,并冻结整个原型及prototype属性。

JavaScript 中原型对象本身不能直接“不可变”,但可以通过组合手段让共享配置在行为上表现为不可修改——关键不是锁住原型链的读取,而是切断外部对共享状态的写入路径,并防止实例遮蔽破坏统一逻辑。
用 Object.defineProperty 锁定原型属性特性
直接赋值到 prototype(如 MyClass.prototype.config = {...})会生成默认可写、可配置的属性,极易被覆盖或删除。正确做法是显式定义描述符:
-
writable: false:阻止通过
instance.config = ...或MyClass.prototype.config = ...修改值 -
configurable: false:禁止
delete MyClass.prototype.config,也禁止后续再调用defineProperty改变描述符 -
enumerable: false(推荐):避免配置项出现在
for...in或Object.keys()中,减少意外暴露
示例:
function App() {}
Object.defineProperty(App.prototype, 'defaultConfig', {
value: { theme: 'light', timeout: 5000 },
writable: false,
configurable: false,
enumerable: false
});
冻结对象内容,防止深层篡改
仅设 writable: false 不足以保护嵌套对象。若配置是普通对象,需用 Object.freeze() 做浅层冻结:
- 冻结后,无法添加、删除或修改自有属性
- 注意:冻结只作用于第一层,子对象仍可变;如需深层不可变,需递归冻结或改用不可变数据结构库
- 建议在定义时就冻结,而非先赋值再冻结,避免中间态被篡改
示例:
const baseConfig = { theme: 'light', features: { darkMode: true } };
Object.freeze(baseConfig); // features 对象本身未冻结
Object.defineProperty(App.prototype, 'defaultConfig', {
value: baseConfig,
writable: false,
configurable: false,
enumerable: false
});
用 getter 统一管控访问逻辑
当需要校验、转换或触发副作用时,getter 比纯数据属性更灵活:
- 所有实例访问
instance.defaultConfig都走同一套逻辑 - 配合
configurable: false,能防止实例通过赋值创建自有属性来遮蔽 getter - 可在 getter 内返回冻结后的副本,避免外部意外修改原始配置
示例:
Object.defineProperty(App.prototype, 'defaultConfig', {
get() {
return Object.freeze({
theme: 'light',
timeout: 5000
});
},
configurable: false,
enumerable: false
});
冻结整个原型并锁定 prototype 属性
单个属性防护不够——攻击者可能遍历原型批量重写,或直接替换整个 prototype:
-
Object.freeze(MyClass.prototype):冻结所有自有属性,等效于对每个属性设writable: false和configurable: false -
Object.defineProperty(MyClass, 'prototype', { configurable: false }):锁死构造函数的prototype属性,防止MyClass.prototype = {}替换 - 这两步应在类定义完成后尽早执行,尤其在模块初始化阶段
不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











