Object.seal 会阻止新增属性,但不冻结已有属性值——它密封对象结构(禁止增删属性、禁止修改属性配置),却允许修改可写属性的值,是介于 preventExtensions 和 freeze 之间的轻量可控方案。

Object.seal 会阻止新增属性,但不冻结已有属性值
直接说结论:Object.seal 正是你需要的——它让对象进入“封印”状态:不能添加新属性、不能删除已有属性、不能重配置属性(比如把 writable 改成 false),但**已存在的可写属性仍能被赋新值**。这和 Object.freeze 的“完全只读”有本质区别。
常见误解是以为 seal 后字段就变只读了,其实不是。比如有个用户资料对象,你希望禁止加 isAdmin 这种新字段,但允许改 name 或 email ——Object.seal 就是为此设计的。
用法很简单,但必须注意属性描述符的初始状态
Object.seal 不会修改已有属性的 writable 或 configurable,它只把整个对象的 extensible 设为 false,并把所有现有属性的 configurable 强制设为 false。所以关键点在于:seal 前,你要确保业务字段本身是 writable: true(默认就是)。
- 如果某个字段是用
Object.defineProperty显式定义且设了writable: false,seal 后依然不能改——这不是 seal 的限制,是定义时就锁死了 - seal 对
get/set访问器属性也生效:不能删、不能重定义,但调用 set 方法赋值仍合法(只要 set 函数本身没逻辑拦截) - 对嵌套对象无效:seal 只作用于目标对象自身,其属性如果是对象,那些对象仍可自由增删改
示例:
const user = { name: 'Alice', role: 'user' };
Object.seal(user);
user.name = 'Bob'; // ✅ 允许
user.role = 'admin'; // ✅ 允许
user.id = 123; // ❌ TypeError: Cannot add property id, object is not extensible
delete user.role; // ❌ TypeError: Cannot delete property 'role'
Object.defineProperty(user, 'name', { writable: false }); // ❌ TypeError: Cannot redefine property: name
和 Object.freeze / Object.preventExtensions 的关键区别
三者都影响 extensible,但深度不同:
-
Object.preventExtensions:只关掉“加新属性”的门,删属性、改描述符、改值全放行 -
Object.seal:在 preventExtensions 基础上,再锁死所有现有属性的configurable(即不能删、不能重定义),但writable不动 -
Object.freeze:在 seal 基础上,再把所有数据属性的writable强制设为false(访问器属性不受影响)
所以如果你只要“防新增 + 保修改”,Object.seal 是最轻量精准的选择;用 freeze 就过度了,用 preventExtensions 又不够严。
实际项目中容易忽略的坑
seal 的效果是浅层的,这点在业务对象常含嵌套结构时特别危险:
- 对
{ profile: { avatar: 'url' } }调用Object.seal,profile属性本身不能删、不能重定义,但user.profile.avatar = 'new-url'和user.profile.tags = []都完全合法 - 想真正保护深层字段,得手动递归 seal,或用库如
immer/deep-freeze(注意 deep-freeze 通常指 freeze,不是 seal) - Vue 2 响应式对象经
Object.seal后可能触发警告:因为 Vue 会尝试给属性加 getter/setter(需configurable: true),而 seal 后不可配,导致响应失效 - TypeScript 类型系统不感知 seal,编译期不会报错,运行时才抛异常,建议配合
as const或自定义类型守卫做额外约束
真正要用好 Object.seal,得清楚它只管“对象顶层的结构稳定性”,不碰“值的可变性”——这两件事本来就是正交的。

















