JS 闭包 (Closure)

概要
是什么
内层函数 + 外层作用域
做什么
在 JavaScript 中,对象并没有像 Java 或 C++ 那样提供真正的私有属性。但通过闭包,我们可以模拟实现私有变量的效果。
- 封闭数据,实现数据私有,使得外部也可以访问函数内部的变量。
其他的一些作用:
- 函数工厂 (Function Factories): 闭包可以用来创建一系列相似但又略有不同的函数
- 回调函数与事件处理 (Callbacks and Event Handlers): 在异步编程,尤其是处理回调函数和事件监听器时,闭包非常有用,它可以帮助我们保持状态。
注意什么
- 潜在的内存消耗 和内存泄露。
- 循环中的变量引用问题。
实例
计数器演示私有变量效果
1 | function outer() { |
函数工厂实例
1 | function createMultiplier(factor) { |
这里,createMultiplier
是一个函数工厂。它接收一个 factor
参数,并返回一个新的函数。这个返回的函数就是一个闭包,它“记住”了传递给 createMultiplier
的 factor
值。
回调函数与事件处理实例
1 | // 假设我们有一个按钮 |
不同于直接定义一个事件监听器,闭包方式可以把状态(clickCount
)和逻辑(按钮点击)封装在一起,避免全局污染,适合模块化和面向对象开发。
在大型项目或多人协作时,推荐使用闭包(第二种写法),这样更安全、可维护性更高。
注意点
内存消耗
由于闭包会使其外部函数的变量一直保存在内存中,如果滥用闭包,或者闭包引用的外部变量占用内存过大,可能会导致内存消耗增加。 详情可见 GC 机制和内存泄露
不过,现代 JavaScript 引擎在垃圾回收方面已经做得相当出色,通常情况下不必过分担心,但了解这一点总是有益的。
循环中的闭包问题
下面是一道面试题,这是一个非常经典的例子,尤其是在使用 var
声明循环变量时。
1 | function createFunctions() { |
为什么都是 3?因为循环体内的匿名函数(闭包)共享同一个词法作用域,它们引用的都是同一个变量 i
。当循环结束后,i
的值变成了 3。所以,当这些函数被调用时,它们访问到的 i
都是 3。
如何解决这个问题?
- 方法一:使用立即执行函数表达式 (IIFE) 创建新的作用域
1 | function createFunctionsFixed_IIFE() { |
- 方法二:使用
let
或const
(ES6 推荐) ES6 引入的let
和const
具有块级作用域,它们在循环中会为每次迭代创建一个新的绑定。
1 | function createFunctionsFixed_Let() { |
- 标题: JS 闭包 (Closure)
- 作者: 三葉Leaves
- 创建于 : 2025-06-01 00:00:00
- 更新于 : 2025-06-07 19:46:13
- 链接: https://blog.oksanye.com/da281a1baf24/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论