JavaScript Stale Closure问题:对象传递时的状态更新困惑
问题解答
核心误区拆解
你混淆了三个关键点:变量的独立身份、闭包捕获的是变量而非即时值、引用指向的更新逻辑,结合你的两个useState实现具体解释:
先明确两个实现的核心差异(模拟常见错误/正确实现)
有BUG的实现(无getState)
function useState(initialValue) { let _val = initialValue; function setVal(newVal) { _val = newVal; } // 直接返回当前的_val值(而非访问它的方法) return [_val, setVal]; } // 使用场景 let foo = { count: 0 }; const [state, setFoo] = useState(foo); setFoo({ count: 1 }); console.log(foo); // 输出 {count:0} console.log(state); // 还是旧的 {count:0}
修复后的实现(带getState)
function useState(initialValue) { let _val = initialValue; function setVal(newVal) { _val = newVal; } // 返回一个能读取最新_val的函数 function getState() { return _val; } return [getState, setVal]; } // 使用场景 let foo = { count: 0 }; const [getState, setFoo] = useState(foo); setFoo({ count: 1 }); console.log(foo); // 依然是 {count:0} console.log(getState()); // 输出最新的 {count:1}
你的疑问逐个解答
外部foo为何没同步变化?
外部foo和useState内部的_val是两个完全独立的变量,只是初始时它们都指向同一个对象内存地址。当你调用setFoo({count:1})时,是给_val赋值了一个新的对象(新内存地址),这只会改变_val的指向,不会影响外部foo的指向——foo依然指向原来的旧对象,所以它的值不会变。闭包内的_val和外部变量指向不同?
初始时指向相同,但当_val被赋值新对象后,指向就不同了。本质是两个变量各自持有自己的引用,互不干扰。赋值新对象后,同一函数的闭包是否共享该内存?
对的。setVal和getState都是useState函数作用域的闭包,它们共享同一个_val变量。当setVal把_val指向新对象的内存地址后,getState调用时会直接读取这个最新的_val引用,所以能拿到更新后的值。第一个实现BUG的根源
第一个实现返回的[_val, setVal]中,_val是当时的即时值(也就是初始的旧对象引用),这个值被存在了外部的state变量里,后续setVal修改_val指向新对象时,外部state不会自动更新——因为它捕获的是旧值,而非_val变量本身。而第二个实现返回的getState是函数,每次调用都会主动读取当前的_val变量,所以能拿到最新状态。
内容的提问来源于stack exchange,提问作者CodeAlien
相关产品推荐
相关产品推荐

