JavaScript 闭包:运行你的代码的静默机制
Source: Dev.to
我回避闭包的时间比我愿意承认的还要长。
这并非有意为之。我只是一再对自己说我懂它们,因为我能重复那句常见的说法:“闭包是记住其词法作用域的函数”。这句话听起来很有说服力,但这还不够。
只有当你不再把闭包当作需要记忆的概念,而是把它们视为代码所表现出的行为时,闭包才真正有意义。
本文并非定义的堆砌,而是尝试以实际 JavaScript 代码中出现的方式来解释闭包,以及它们为何比大多数初学者意识到的更为重要。
Source:
当你编写函数时不会创建闭包
闭包并不是在函数声明时创建的。它们在函数被执行且继续引用本应已经不存在的外部作用域变量时才会出现。
function createCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const counter = createCounter();
乍一看,count 应该在 createCounter 执行完毕后就消失了。但事实并非如此。内部函数仍然可以访问它。
为什么会这样?因为 JavaScript 并不会把值复制进函数内部,而是保留对创建函数时所在环境的引用。这个环境,也就是词法环境,正是我们通常所说的闭包。
闭包是关于内存,而不是语法
最有用的思维转变之一是认识到闭包本质上是一个 内存模型,而不是语法特性。JavaScript 引擎只要 仍然有东西能够访问,就会让变量保持存活。
在上面的例子中:
count仍然是可达的- 因此它会留在内存中
- 因此它的值在函数调用之间得以持久化
仅此而已。没有魔法。一旦你以这种方式看待闭包,许多令人困惑的行为就会变得可预测。
为什么闭包一开始会让人感到困惑
闭包打破了许多人无意识遵循的直觉规则:
“当函数执行完毕,里面的一切都会消失。”
这条规则大多数情况下是对的——直到它不再适用。闭包是证明这条规则的例外。它们迫使你思考谁仍然持有对什么的引用。这也是闭包在循环中常成为 bug 源头的原因。
for (var i = 0; i {
console.log(i);
}, 1000);
}
人们期待得到 0, 1, 2。实际得到 3, 3, 3。这并不是因为 JavaScript 有问题,而是因为这三个函数都闭包了同一个变量,而不是三个不同的值。一旦你理解了闭包,这种行为就不再令人惊讶。
闭包无处不在,即使你没有注意到
如果你曾经:
- 在 React 中使用
useState - 编写过回调函数
- 使用
setTimeout或setInterval - 处理事件监听器
- 实现过柯里化或部分应用
那么你已经使用了闭包。
框架大量依赖闭包,因为它们可以让状态存在而无需全局变量——这是一大设计优势。例如,React Hooks 就依赖闭包在渲染之间“记住”值,同时仍然保持函数在外观上的纯粹性。
闭包实现无需类的封装
在 ES6 类流行之前,闭包是 JavaScript 中创建私有状态的主要方式之一。即使在今天,许多开发者仍然更倾向于使用闭包而不是类来实现简单的封装:
function createBankAccount() {
let balance = 0;
return {
deposit(amount) {
balance += amount;
},
getBalance() {
return balance;
}
};
}
在这里,balance 完全无法从外部访问。没有专门的隐私关键字;隐私是自然地由闭包产生的。这种模式至今仍表现得非常出色。
缺点:闭包可能让对象存活时间过长
闭包功能强大,但并非没有代价。由于它们会保持引用存活,可能会不小心让大型对象在内存中停留的时间超过必要——尤其是在浏览器或服务器等长时间运行的应用中。由闭包导致的内存泄漏往往很微妙且容易被误解。问题不在于闭包本身,而是忘记了被引用的对象会一直存活。
理解闭包能够帮助你比记忆垃圾回收规则更好地推理内存使用情况。
更好地解释闭包(一句话)
闭包是指当函数保持对创建时已存在的变量的实时连接时发生的情况。
这句话并不诗意,但却是准确的。
进一步阅读并加深理解
如果你想更深入了解,这里有一些真正有用的方向可以探索:
- MDN 对词法环境和执行上下文的解释
- 探讨 JavaScript 引擎内部(尤其是 V8)的文章
- 关于 Scheme 和 Lisp 中闭包的函数式编程讨论
- 关于词法作用域和环境模型的学术论文(arXiv 在这里很有帮助)
闭包并非 JavaScript 独有。深入理解它们可以让学习其他语言更加轻松。
最后思考
闭包并不是你“一次性学会”的东西。它们是随着你写 JavaScript 的时间越长,你会逐渐更清晰地注意到的东西。到某个时刻,它们不再是神秘的面试话题,而是成为你不假思索就依赖的安静、可靠的工具。这通常是你终于真正理解它们的标志。