writable属性描述符仅限制直接赋值操作,不递归作用于嵌套属性;设为false后,obj.prop不可重赋值,但obj.prop.child仍可修改,因其是独立属性,需单独设置writable保护。

writable 属性描述符本身不会“失效”,它只对**直接定义的属性**生效。所谓“深层属性 writable 不起作用”,本质是误解了 writable 的作用范围——它不递归、不继承、不自动传播到嵌套对象中。
writable 只保护当前层级的属性值
当你对 obj.a 设置 writable: false,只是锁定了 obj.a 这个属性的赋值行为(比如 obj.a = 100 会被拦截),但完全不影响 obj.a.b 或 obj.a.b.c 的可写性。
因为 obj.a.b 是另一个独立属性,它的 writable 状态由它自己所在的对象(即 obj.a)决定,与外层 obj 无关。
-
示例:
Object.defineProperty(obj, 'config', { value: { timeout: 5000 }, writable: false })→obj.config不能被重新赋值,但obj.config.timeout = 10000依然成功 - 原因:writable 控制的是
obj.config这个“引用值”能否被替换,不是控制它指向的对象内部是否可变
深层对象未受保护的常见场景
以下操作看似在改“同一个配置”,实际触发的是不同层级的赋值逻辑:
-
obj.data = { x: 1 }→ 受obj.data的 writable 控制 -
obj.data.x = 2→ 受obj.data对象上x属性的 writable 控制(默认为 true) -
obj.data.list.push(3)→ 不涉及赋值,而是调用方法,writable 完全不干预
也就是说,只要嵌套对象本身没被显式设置 writable: false,它的所有自有属性都保持默认可写。
如何真正保护深层结构?
没有一键开启“深层 writable”的机制,必须逐层或批量处理:
-
手动逐层定义: 对每个关键嵌套属性都调用
Object.defineProperty,例如Object.defineProperty(obj.data, 'timeout', { value: 5000, writable: false }) -
递归冻结:
Object.freeze()会递归冻结所有自有属性(设writable: false+configurable: false),但仅限于已存在的属性,新增属性不受限 -
深度 seal / freeze 封装: 自定义函数遍历对象树,对每个对象调用
Object.freeze;注意循环引用需处理,且无法冻结 getter/setter 或 Symbol 属性
为什么不能靠父级 writable 拦截深层修改?
JavaScript 引擎的设计原则是:属性描述符只约束对该属性的**直接赋值操作**(=),不介入任何间接访问路径(点号、方括号、方法调用、原型链查找等)。这是语言底层行为,不是 bug,而是有意为之的轻量级控制机制。
换句话说:writable: false 是一道门锁,只锁门把手;它不管屋里的人能不能挪椅子、拆柜子、甚至换地板——除非你给每件家具也单独上锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











