Ja vaScript属性描述符writable对赋值行为的限制

在Ja vaScript的对象世界里,给一个属性加上“只读”锁,听起来是个简单的操作。但你真的了解这把锁的精确作用范围吗?今天,我们就来深入聊聊writable: false这个描述符——它远不止“不能改”那么简单。
核心结论先摆在这里:当一个属性的writable被设为false,直接通过赋值操作符(=)去修改它的值,这条路就走不通了。在严格模式下,浏览器会毫不客气地抛出一个TypeError;而在非严格模式下,它则会选择“静默失败”——代码照常运行,不报错,但属性的值纹丝不动。
writable: false 时的赋值表现
这里有个关键细节需要划重点:writable: false限制的仅仅是常规的赋值操作(obj.prop = newValue)。它并不妨碍你通过Object.defineProperty或Object.defineProperties这些“元编程”工具去修改属性描述符本身。当然,这么做有个大前提:该属性的configurable描述符必须是true。
具体表现,我们可以看几个典型场景:
- 非严格模式:执行
obj.x = 100,控制台风平浪静,但回头检查obj.x,你会发现它还是原来的值。 - 严格模式:同样的
obj.x = 100,会立即触发TypeError: Cannot assign to read only property 'x',赋值被明确拒绝。 - 试图用defineProperty覆盖:直接调用
Object.defineProperty(obj, 'x', { value: 200 })也会失败,除非你同时将writable设为true,或者该属性原本就是configurable: true,允许你先修改描述符。
与 const 声明的区别
很多人容易把writable: false和const声明混淆。其实,它们守护的“阵地”完全不同。
writable: false是对象属性级别的写保护。它锁定的,是某个特定属性的值。而const,是变量(或常量)绑定级别的不可重新赋值。它锁定的,是变量名与那个值(或引用)之间的绑定关系。
举个例子就清楚了:
const obj = { x: 1 };这里,const意味着你不能让obj这个变量名指向另一个对象(比如obj = {}会报错)。但是,obj.x = 2这个操作完全可行——除非x属性本身被设置了writable: false。- 反过来,
Object.defineProperty(obj, 'x', { value: 1, writable: false })一旦执行,那么无论obj本身是用const、let还是var声明的,obj.x = 2这个赋值操作都会被禁止。
简单说,const管的是“装东西的篮子不能换”,而writable: false管的是“篮子里的某个特定物品不能换”。
如何检测和修改 writable 状态
那么,在实际开发中,我们怎么知道一个属性是不是只读的?又有没有办法“解锁”它呢?
答案是肯定的。使用Object.getOwnPropertyDescriptor(obj, 'prop'),这个方法会返回一个描述符对象,里面清清楚楚地列着writable、configurable、enumerable和value的当前状态。
至于修改,关键在于另一个描述符:configurable。它的名字已经说明了一切——它决定了属性的配置是否可被更改。
- 如果
configurable: true,那么恭喜,你可以随时通过Object.defineProperty,将writable从false改回true,从而重新获得赋值权限。 - 但如果
configurable: false,情况就严峻了。这意味着属性被“永久锁定”,它的writable状态(以及其他描述符)再也无法被修改。一个既writable: false又configurable: false的属性,就成了一个真正意义上的“铁板一块”。
常见误用场景
最后,我们来看几个开发者常踩的坑。最大的误解莫过于:以为给一个对象设置几个writable: false的属性,就等于“冻结”了整个对象。
事实远非如此。writable: false的保护是精确到单个属性的。
- 假设对象
obj的属性a是writable: false,那么obj.a = 1会失败。但这丝毫不影响你给对象新增一个属性b(obj.b = 2),或者修改另一个writable: true的属性。 - 如果你想要的是更全面的保护,Ja vaScript提供了更高级的“封印”方法:
Object.preventExtensions(obj):禁止对象添加新属性。
Object.seal(obj):在preventExtensions的基础上,额外将所有现有属性设为configurable: false(但writable为true的属性仍可修改值)。
Object.freeze(obj):这是最高级别的“冻结”。它在seal的基础上,进一步将所有自有数据属性的writable设为false。一个被freeze的对象,其结构(不能增删属性)和内容(不能修改值)都被彻底锁定了。
所以,下次当你需要保护对象数据时,先想清楚:你需要的,究竟是给个别贵重物品上锁(writable: false),还是给整个保险箱贴上封条(Object.freeze)?
