JavaScript 闭包详解
你在 JavaScript 中每天都在使用闭包。你可能在 React hooks、事件监听器和 Node.js 回调函数中使用它们。许多开发者在理论上感到困惑,但一旦你掌握了其中的模式,这个概念其实很简单。
要理解闭包,你必须先理解词法作用域(lexical scope)。
词法作用域意味着一个函数可以访问其自身作用域以及定义它时所在的任何外部作用域中的变量。它取决于你编写代码的位置,而不是你调用代码的位置。
闭包是一个即使在其原始作用域运行结束之后,仍能记住该作用域中变量的函数。
把函数想象成背着一个背包。当函数被创建时,它会将环境中所需的每个变量都装进那个背包里。函数无论走到哪里,都会带着那个背包。
以下是闭包在实践中的工作方式:
私有变量:你可以向外界隐藏数据。以银行账户为例,余额(balance)变量保持私有。你只能通过特定的方法(如 deposit 或 withdraw)来修改它。这可以防止直接访问敏感数据。
函数工厂:你可以创建能够生成其他函数的函数。一个乘法器工厂可以创建一个翻倍函数或一个三倍函数。每个新生成的函数都会在其背包中保留各自特定的乘数。
事件监听器:当你为按钮绑定点击事件时,处理函数(handler)会记住来自设置函数(setup function)的数据。即使设置函数执行完毕,监听器仍会保留这些数据。
React Hooks:每当你使用
useState或useEffect时,你都在使用闭包。React 中一个常见的 Bug 是闭包捕获了旧的状态值(state),这被称为陈旧闭包(stale closure)。
注意内存使用情况。由于闭包持有对变量的实时引用,只要闭包存在,引擎就无法通过垃圾回收(garbage collection)来清理这些变量。如果你不需要长时间持有大型对象,请避免在闭包中保留它们。
总结:
- 词法作用域定义了访问权限。
- 闭包使这种访问具有持久性。
- 函数通过背包携带其环境。
- 在循环中使用
let而不是var,以避免作用域相关的 Bug。
来源:https://dev.to/digitalunicon/javascript-closures-explained-with-examples-339f