很多人第一次接触 Object.preventExtensions() 时,容易把它理解成“对象彻底锁死”。实际并不是这样:它限制的是对象自身新增属性的能力,而不是整个继承链。本文围绕几个最容易混淆的点展开,说明它到底拦住了什么、为什么原型上的改动仍然生效,以及如果你真正想限制原型,还需要额外做哪些处理。
preventExtensions 到底限制了什么
Object.preventExtensions() 仅阻止对象本身再新增新的自有属性,不会影响原型链操作;可修改、删除已有属性,也可继续操作 __proto__、prototype 及原型对象上的属性。这是它的设计边界:只封禁“在当前对象上定义新属性”,不干涉继承机制。
调用之后,下面这些行为的结果可以直接这样理解:
obj.existing = 'ok':允许,属于修改已有属性delete obj.existing:允许,属于删除已有属性obj.__proto__.shared = 'from proto':允许,本质是在改原型对象Object.getPrototypeOf(obj).newOnProto = true:允许,依然是给原型加属性obj.newProp = 123:不允许,严格模式下报错,非严格模式下静默失败
换句话说,“不可扩展”不等于“不可修改”,更不等于“不可继承新内容”。对象自己不能长出新字段,但它背后的原型链仍然是开放的。
为什么实例被限制后,原型上的新方法还能用
这类问题在构造函数或类实例上最常见。对实例调用 Object.preventExtensions(),被限制的是这个实例对象本身,不是构造函数的 prototype 对象。因此,后来再往原型上补方法,实例依然能继承到。
function Person() {}
const p = new Person();
Object.preventExtensions(p);
// ✅ 完全合法:修改 Person.prototype
Person.prototype.sayHi = function() { return 'hi'; };
console.log(p.sayHi()); // 'hi' —— 继承自原型,不受限制
// ✅ 也可直接改 p 的原型(不推荐但可行)
p.__proto__.age = 30;
console.log(p.age); // 30
上面的关键点在于:p 没有新增 sayHi 或 age 这两个自有属性,它只是继续沿着原型链取值。所以即便 p 已经被设为不可扩展,这段代码仍然成立。
这也解释了一个常见误判:有些人看到实例“还能访问新增的方法”,就以为 preventExtensions 没生效。实际上它已经生效,只是你修改的是原型,不是实例本身。
如果要限制原型,必须单独处理
preventExtensions 不会自动保护原型对象。如果你的目标是不让别人继续往原型上挂方法或属性,就必须明确对原型对象本身操作。

Object.preventExtensions(Person.prototype):禁止再向Person.prototype新增属性Object.seal(Person.prototype):进一步禁止删除属性和修改属性描述符配置Object.freeze(Person.prototype):限制最强,不能增删改,属性值也不可变,但这里仍是浅冻结
需要注意,这些调用都只作用于传入的那个目标对象。例如你冻结的是 Person.prototype,那么受影响的是原型对象本身,而不是实例 p 的扩展状态;反过来,对 p 调用 Object.preventExtensions(p),也不会顺带锁住 Person.prototype。
因此在实际使用里,最好先明确你的防护目标到底是谁:是实例不能再长新字段,还是原型不能再被打补丁。两者不是一回事,处理方式也不同。
几个容易混淆的概念
讨论到这里,最值得顺手澄清的是下面三组概念:
__proto__是实例的内部原型引用,可读写;现代写法更推荐使用Object.getPrototypeOf和Object.setPrototypeOfprototype是函数对象上的属性,用来在创建实例时决定实例的__proto__指向,只有函数对象才有这个属性preventExtensions不会改变原型链结构,也不会阻止你使用Object.create(null)这类方式创建无原型对象
如果把这些概念混成一团,就很容易得出错误结论,比如把“实例不可扩展”误认为“原型不可修改”,或者把“原型新增方法可访问”误认为“实例新增属性也应该可行”。
实际使用时可以怎么判断
可以用一句话快速判断:看你是在改对象自身,还是在改它的原型对象。 如果是前者,Object.preventExtensions() 会生效;如果是后者,它通常不会拦你。
所以在排查行为时,可以按这个顺序确认:
- 当前新增的是不是对象自己的属性
- 代码是不是实际上写到了
Object.getPrototypeOf(obj)、__proto__或构造函数的prototype上 - 你要保护的目标,是否已经被单独执行过
preventExtensions、seal或freeze
把边界看清后,Object.preventExtensions() 的行为并不反直觉:它只是阻止对象继续扩展自身结构,但不会替你接管原型链的可变性。










