JavaScript 闭包的形成

6 min read

在 JavaScript 中,闭包是一种特殊的结构,它允许一个函数访问并操作它被声明时的词法作用域中的变量,即使那个函数在一个不同的作用域被执行。当您在一个函数内部创建另一个函数时,通常就会形成闭包。

在上面的 setTimeout 例子中,闭包的形成是因为 setTimeout 的回调函数捕获了定义时作用域中的 count 变量。即使是在计时器的延迟时间之后,这个函数依旧可以访问到当时的 count 变量。

闭包的形成通常具有以下特征:

  1. 在一个函数内部定义另一个函数:内部函数可以访问外部函数的变量。
  2. 外部函数返回内部函数:这允许内部函数在外部函数执行完毕后继续存在。
  3. 变量在内部函数中被引用:即便外部函数已经执行完毕,这些变量仍然可用。

在 React 的上下文中,每次组件重新渲染都会执行其函数体,创建新的函数(包括事件处理器和效果的回调)和新的闭包。如果这些函数依赖于组件的状态或属性,并且被异步调用(如 setTimeoutsetInterval 或者异步请求的回调),它们会引用创建时的状态或属性值,而不是最新的值。

为了解决闭包陷阱,确保访问到最新的状态或属性,可以使用以下方法:

  • 函数式更新:当状态更新需要当前状态时,可以提供一个函数给状态更新函数,React 会确保这个函数接收到的状态是最新的。
  • 使用 useRefuseRef 可以存储一个可变的值,其在组件的整个生命周期内保持不变,而 .current 属性可以用来获取最新的状态值。
  • 将状态或函数作为依赖项:在 useEffect 或其他 Hooks 中,通过依赖数组跟踪状态或属性的变化。

setTimeout 的例子中,我们可以通过使用 useRef 或者函数式更新的方式来获取最新的状态,这样就不会捕获过时的状态值了。