原型上的属性描述符只能在原型对象本身设置,不能通过实例调用 object.defineproperty 修改;否则会创建遮蔽属性。应直接操作 person.prototype 或使用 class 的 getter/setter,配合 configurable: false 和 object.freeze 可防止遮蔽与篡改。

原型上的属性描述符不能直接通过实例调用 Object.defineProperty 来修改,因为该方法只作用于对象自身的属性,不触及原型链。要控制原型上属性的访问与修改,必须在原型对象本身上定义或调整描述符。
原型属性只能在原型对象上设置描述符
比如你有一个构造函数 Person,它的原型上有 getName 方法:
错误做法(试图在实例上改原型属性):
const p = new Person();Object.defineProperty(p, 'getName', { writable: false }); // 无效!这只是给 p 自身加了个同名遮蔽属性
正确做法(直接操作原型):
- 在
Person.prototype上使用Object.defineProperty - 或者用 class 语法配合 getter/setter 定义访问器属性
- 确保没有意外地在实例上覆盖了原型属性(即避免
p.getName = ...)
访问器描述符是控制原型属性行为的常用方式
在原型上定义 get 或 set,可统一拦截所有实例对该属性的读写。例如:
let _name = 'default';<br>Object.defineProperty(Person.prototype, 'name', {<br> get() { return _name; },<br> set(val) { if (typeof val === 'string') _name = val; },<br> enumerable: true,<br> configurable: false<br>});
这样每个 new Person() 实例访问 .name 都会走这个统一逻辑,且无法被实例自身属性轻易绕过(除非显式设 p.name = ...,但此时若原型属性 configurable: false,该赋值会静默失败或报错,取决于严格模式)。
注意遮蔽(shadowing)与不可逆配置
如果在实例上用 Object.defineProperty 添加同名属性,且 configurable: false,它会永久遮蔽原型上的同名属性,且无法再删除或还原:
- 原型有
get name() { ... },实例执行Object.defineProperty(p, 'name', { value: 'Alice', configurable: false })→ 原型访问器失效 - 之后无法通过
delete p.name恢复原型行为(configurable: false禁止删除) - 也无法再用
defineProperty把它改回访问器(configurable: false后禁止任何描述符变更)
批量冻结原型属性行为
若需让整个原型上的自有属性都不可写、不可配置,可用 Object.freeze(Person.prototype):
- 它等价于对原型上每个自有属性设
writable: false和configurable: false - 不影响实例自身属性,也不影响原型链更上层(如
Object.prototype) - 后续在实例上调用
p.toString = ...仍可成功(因为toString来自更上层,且未被冻结)











