0%

JavaScript 闭包详解

什么是闭包?

闭包是函数和其词法环境的组合。

1
2
3
4
5
6
7
8
9
10
11
12
function outer() {
const value = 'closure'

function inner() {
console.log(value)
}

return inner
}

const func = outer()
func() // 'closure'

作用

  • 封装私有变量
  • 创建工厂函数
  • 实现模块模式

经典例子

计数器

1
2
3
4
5
6
7
8
9
10
11
12
function createCounter() {
let count = 0

return {
increment: () => ++count,
decrement: () => --count,
getCount: () => count
}
}

const counter = createCounter()
counter.increment() // 1

私有变量

1
2
3
4
5
6
7
8
function Person(name) {
let _name = name

return {
getName: () => _name,
setName: (newName) => _name = newName
}
}

注意事项

  • 内存泄漏:闭包可能导致变量无法释放
  • 性能:过度使用影响性能

总结

闭包是 JavaScript 最强大的特性之一,它赋予函数维护私有状态的能力,也是模块化、迭代器、回调和许多高级模式的基础。合理使用闭包可以写出简洁优雅的代码,但滥用则可能导致内存泄漏和调试困难。

闭包在模块化中的应用

1
2
3
4
5
6
const counter = (function() {
let count = 0;
return function() {
return ++count;
};
})();

性能和优化

  • 避免在循环中创建不必要的闭包
  • 使用 let/const 代替 var 防止意外共享
  • 释放引用:将不再使用的闭包赋值为 null

闭包与事件处理

1
2
3
4
button.addEventListener('click', (function() {
let clicks = 0;
return () => { console.log(++clicks); }
})());

常见误区

  • 认为闭包永远存在:只要有引用就会保持
  • 认为闭包创造全局变量:闭包本身是局部的

调试闭包

  • 使用 Chrome DevTools 的 Scope 窗口查看闭包变量
  • heap snapshot 分析函数闭包

小结

掌握闭包需要理解作用域链与垃圾回收的交互。不断练习示例,并阅读开源库源码(如 jQuery、Lodash)以体会闭包的实际用法。

闭包是 JavaScript 强大特性。理解它能写出更好代码,但要小心内存使用。

练习题

  1. 写出一个闭包实现的缓存函数。
  2. 使用闭包创建一个计时器。

扩展阅读

  • JavaScript 权威指南中的闭包章节
  • Paul Irish 关于闭包的演讲

代码审查建议

在审查代码时留意函数内部是否创建了不必要的闭包,以及是否安全释放了外部引用。

练习答案示例

1
2
3
4
5
6
7
function memoize(fn) {
const cache = {};
return function(arg) {
if (cache[arg]) return cache[arg];
return cache[arg] = fn(arg);
};
}

补充闭包行 1
补充闭包行 2
补充闭包行 3
补充闭包行 4
补充闭包行 5
补充闭包行 6
补充闭包行 7
补充闭包行 8
补充闭包行 9
补充闭包行 10