要真正“锁死”继承体系中的核心属性,必须在原型对象上用Object.defineProperty同时设置writable: false和configurable: false;仅设其一不足以防止修改或删除,且不可在实例上操作以免产生遮蔽属性。

要让继承体系中某个核心属性真正“锁死”,不能只靠赋值或简单挂载,关键是在原型对象上用 Object.defineProperty 精确设置其描述符,同时配合不可配置(configurable: false)与不可写(writable: false)两个标志。这能确保该属性既不能被修改值,也不能被删除、重定义或切换为访问器。
必须在原型对象上操作,而非实例
所有控制都必须直接作用于原型本身。如果误在实例上调用 Object.defineProperty 设置同名属性,只会创建一个遮蔽属性(shadow property),原型上的原定义依然存在且未受保护。
- ✅ 正确:在
MyComponent.prototype上设置 - ❌ 错误:对
const inst = new MyComponent()调用Object.defineProperty(inst, 'version', {...})
设置不可写 + 不可配置的最小安全组合
仅设 writable: false 不够——它仍允许用 delete 删除该属性,或用 defineProperty 改成 getter/setter;仅设 configurable: false 也不够——它不阻止赋值(除非同时 writable: false)。二者必须同时启用:
-
writable: false:禁止通过obj.prop = newValue修改值(严格模式报错,非严格模式静默失败) -
configurable: false:禁止删除该属性、禁止再次调用defineProperty修改其任何描述符(包括writable、enumerable、get/set) - 建议显式加上
enumerable: true(如需遍历可见)或false(如属内部字段)
示例:
Object.defineProperty(MyComponent.prototype, 'apiVersion', {value: 'v2.1',
writable: false,
configurable: false,
enumerable: true
});
避免遮蔽导致的“伪锁定”
一旦某个实例自身拥有了同名属性(例如 inst.apiVersion = 'v3'),它就永久屏蔽了原型上的定义。此时即使原型属性已锁定,该实例也读不到它,更谈不上受控。
- 运行前可用
Object.getOwnPropertyNames(inst)检查是否已有自有属性 - 若需恢复原型值,只能
delete inst.apiVersion清除自有属性(前提是它没被设为configurable: false) - 真正防遮蔽,需配合
Object.freeze(MyComponent.prototype)冻结整个原型对象
冻结整条原型链以强化防护
单个属性锁定是点状防护;若需保障整套核心接口不被篡改,应冻结整个原型对象:
-
Object.freeze(MyComponent.prototype)会递归将所有自有属性设为writable: false和configurable: false - 不影响实例自身属性,也不影响
Object.prototype等上游原型 - 应在构造函数定义后、首次实例化前执行,确保无运行时污染


















