JavaScript 闭包
闭包是指函数能够访问其词法作用域,即使该函数在其作用域之外执行。
核心概念
JavaScript
function outer() {
const message = 'Hello';
function inner() {
console.log(message); // 访问外部变量
}
return inner;
}
const fn = outer();
fn(); // 'Hello' - 闭包保留了对 message 的引用
词法作用域
闭包基于词法作用域(静态作用域),函数定义时确定作用域:
JavaScript
const value = 'global';
function foo() {
const value = 'local';
return function() {
console.log(value); // 'local' - 定义时确定
};
}
const fn = foo();
fn(); // 'local'
常见应用场景
数据私有化
JavaScript
function createCounter() {
let count = 0; // 私有变量
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
const counter = createCounter();
counter.increment(); // 1
counter.increment(); // 2
counter.getCount(); // 2
// count 不可直接访问
函数工厂
JavaScript
function createMultiplier(factor) {
return function(number) {
return number * factor;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
double(5); // 10
triple(5); // 15
回调与事件处理
JavaScript
function setupButtons() {
const buttons = document.querySelectorAll('button');
buttons.forEach((button, index) => {
button.addEventListener('click', () => {
console.log(`Button ${index} clicked`);
});
});
}
偏函数应用
JavaScript
function fetch(url) {
return function(path, options) {
return fetch(`${url}${path}`, options);
};
}
const apiFetch = fetch('https://api.example.com');
apiFetch('/users'); // https://api.example.com/users
循环中的闭包陷阱
问题代码
JavaScript
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 输出: 3, 3, 3
}, 100);
}
解决方案
JavaScript
// 方案1:使用 let(块级作用域)
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // 输出: 0, 1, 2
}, 100);
}
// 方案2:IIFE 创建闭包
for (var i = 0; i < 3; i++) {
((j) => {
setTimeout(() => {
console.log(j); // 输出: 0, 1, 2
}, 100);
})(i);
}
内存管理
闭包会保持对外部变量的引用,可能导致内存占用:
JavaScript
// 不好的做法:大对象被闭包持有
function process() {
const largeData = new Array(1000000).fill('x');
return function() {
console.log(largeData.length); // largeData 无法释放
};
}
// 好的做法:只保留需要的数据
function process() {
const largeData = new Array(1000000).fill('x');
const length = largeData.length;
// largeData 可被回收
return function() {
console.log(length);
};
}
闭包 vs 作用域
| 概念 | 说明 |
|---|---|
| 作用域 | 变量的可访问范围 |
| 词法作用域 | 定义时确定的作用域 |
| 闭包 | 函数 + 其词法作用域 |
要点总结
- 闭包允许函数访问定义时的外部变量
- 用途:数据私有化、函数工厂、回调函数
- 循环中使用
let或 IIFE 避免闭包陷阱 - 注意内存管理,避免持有不需要的数据
- 闭包是函数式编程的基础机制
📝 发现内容有误?点击此处直接编辑