要让类的原型结构真正稳定、不可篡改,必须通过 Object.defineProperty 设置 writable: false 和 configurable: false 锁定属性,并用 Object.freeze(MyClass.prototype) 和 Object.defineProperty(MyClass, 'prototype', {configurable: false}) 防止原型被替换或批量修改,同时优先使用 configurable: false 的 getter/setter 防范实例遮蔽。

要让类的原型结构真正稳定、不可篡改,不能只靠“不写错代码”来预防,而必须从属性描述符底层锁死行为。核心是控制原型上方法和属性的 writable 和 configurable 特性,并防范实例遮蔽。
在原型上定义不可覆盖的属性
直接赋值到 prototype(如 MyClass.prototype.method = function(){})会生成可写、可配置的默认属性,极易被意外覆盖或删除。正确做法是用 Object.defineProperty 显式声明:
- 设置
writable: false:阻止通过instance.method = ...覆盖方法 - 设置
configurable: false:禁止delete instance.method,也禁止后续再调用defineProperty修改描述符 - 保持
enumerable: true(按需):方便调试时在for...in或Object.keys中看到该方法
示例:
function MyClass() {}<br>Object.defineProperty(MyClass.prototype, 'id', {<br> value: Symbol('id'),<br> writable: false,<br> configurable: false,<br> enumerable: false<br>});
统一拦截读写:用 getter/setter 控制访问逻辑
当需要对所有实例的某个属性做统一校验、计算或副作用管理时,访问器描述符比数据描述符更灵活:
- 在原型上定义
get和set,所有实例访问该属性都会触发同一套逻辑 - 配合
configurable: false,可防止实例通过赋值创建自有属性遮蔽访问器 - 若在
set中抛出错误或静默忽略非法值,就能实现强约束
注意:一旦实例执行了 p.prop = val,且原型属性未设 configurable: false,就会生成遮蔽属性,访问器失效。
冻结整个原型对象,杜绝批量篡改
单个属性防护不够——攻击者可能遍历原型、批量重写方法,或替换整个 prototype。应尽早执行:
-
Object.freeze(MyClass.prototype):等效于对原型所有自有属性设writable: false和configurable: false -
Object.defineProperty(MyClass, 'prototype', { configurable: false }):锁死构造函数的prototype属性本身,防止MyClass.prototype = {...}替换 - 对内置原型(
Array.prototype、String.prototype等)同样冻结,尤其在应用入口最早期执行
防御实例遮蔽,守住原型链有效性
即使原型属性已设 writable: false,普通赋值仍会在实例上创建同名自有属性,从而永久遮蔽原型行为。应对策略包括:
- 优先使用访问器 +
configurable: false,使遮蔽赋值在严格模式下报错、非严格模式下静默失败 - 避免在构造函数中给实例添加与原型同名的属性(如
this.name = ...),否则天然形成遮蔽 - 关键类可结合
Object.preventExtensions(instance),但需权衡开发灵活性——它会阻止任何新属性添加,包括合法的临时状态

















