Object.freeze冻结对象后变量重赋值不报错原因与解决方案
JavaScript 深度冻结相关问题解答
问题1:deepFreeze 后执行obj2 = 5;为什么不抛出错误?
核心原因是**Object.freeze(包括递归实现的deepFreeze深度冻结)的作用范围是对象值本身,完全无法影响持有对象引用的变量绑定**。
你可以简单理解两者的边界:
- 用
let obj2 = {internal: {a: null}}声明变量时,obj2本质是一个存储了对象内存地址的引用指针,存在栈内存的变量空间里;对应的对象是存在堆内存里的独立值。 deepFreeze(obj2)的操作,是顺着指针找到堆里的对象,把对象本身锁死:禁止增删属性、禁止修改属性描述符、禁止修改属性值,递归把所有嵌套的引用类型值也全部锁死。这个操作从头到尾没有修改过obj2这个指针本身的读写规则。
执行obj2 = 5时,做的事情是直接把obj2指针里存的地址擦掉,改成原始值5,全程没有触碰那个已经被冻结的堆内存对象,自然不会触发冻结相关的报错。
你注释里的obj2.internal.a = 'anotherValue'会在严格模式下报错,是因为这个操作是顺着指针找到被冻结的内部对象,尝试修改它的属性,属于deepFreeze的管控范围。
问题2:如何让obj2 = 5;赋值时抛出错误?
首先要明确:var/let声明的变量默认是允许重新赋值的,JS 仅对全局作用域下var声明的变量暴露了修改绑定规则的API,其余场景没有原生支持,分情况处理:
- 全局作用域下用
var声明的obj2
全局作用域的var声明会自动成为全局对象(浏览器环境为window,Node.js 环境为globalThis)的自有属性,你可以通过属性配置把它改成不可写,严格模式下重新赋值就会抛出TypeError:"use strict" // 注意:仅全局作用域下var声明的变量会挂在全局对象上 var obj2 = { internal: { a: null } }; deepFreeze(obj2); // 全局var场景下加这一行即可 Object.defineProperty(globalThis, 'obj2', { writable: false, configurable: false }) obj2 = 5; // 严格模式下直接抛错 - 其余场景:包括非全局作用域的
var、所有作用域下的let声明的obj2
这类变量的绑定存储在词法环境的声明式记录中,JS 没有提供标准API修改这类绑定的可写性,无法直接让原生let/var声明的这类变量禁止重赋值,有两种可行的替代方案:- 最简洁的方案是直接把
let/var换成const声明。const天生禁止重新给变量绑定赋值,严格模式下重赋值直接抛错;注意const仅保证变量不能被重新赋值,不会冻结指向的对象内容,配合deepFreeze就能同时实现「变量不能改指向」「指向的对象内容不能修改」的完全常量效果,也是工业界的通用实践。 - 如果一定要保留
var/let的使用语义,可以把值托管到一个被冻结的持有对象上,所有访问通过持有对象的只读属性完成:"use strict" function localScope() { // 持有对象被深度冻结,属性不可修改 const holder = deepFreeze({ obj2: { internal: {a: null} } }) // 后续所有操作通过holder.obj2访问 holder.obj2.internal.a = 1; // 抛错:对象被冻结不可修改属性 holder.obj2 = 5; // 抛错:持有对象被冻结,属性不可重写 }
- 最简洁的方案是直接把
内容的提问来源于stack exchange,提问作者nikhil
相关产品推荐
相关产品推荐

