在 JavaScript 中,闭包(Closure) 是指一个函数能够记住并访问它的词法作用域(Lexical Scope),即使这个函数在其作用域之外被执行。闭包的特性使得它在以下情况下非常有用:
适用情况
闭包范例
范例 1:封装私有数据
使用闭包模拟私有属性,让外部无法直接修改内部数据。
function createCounter() {
let count = 0; // 私有变量
return {
increment() {
count++;
},
decrement() {
count--;
},
getCount() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.getCount()); // 0
counter.increment();
counter.increment();
console.log(counter.getCount()); // 2
counter.decrement();
console.log(counter.getCount()); // 1
范例 2:模拟状态的工厂函数
使用闭包创建具有不同初始状态的计数器。
function createCounter(initialValue) {
let count = initialValue;
return function () {
count++;
return count;
};
}
const counterA = createCounter(5);
const counterB = createCounter(10);
console.log(counterA()); // 6
console.log(counterA()); // 7
console.log(counterB()); // 11
console.log(counterB()); // 12
范例 3:处理异步操作
闭包在回调函数中保存上下文数据。
function fetchData(url) {
let cache = {};
return function () {
if (cache[url]) {
console.log("Fetching from cache:", cache[url]);
return cache[url];
} else {
console.log("Fetching from server...");
const data = `Data from ${url}`; // 模拟服务器数据
cache[url] = data;
return data;
}
};
}
const getUserData = fetchData("https://api.example.com/user");
console.log(getUserData()); // Fetching from server...
console.log(getUserData()); // Fetching from cache: Data from https://api.example.com/user
范例 4:函数柯里化
闭包让函数变成更灵活的形式。
function multiply(a) {
return function (b) {
return a * b;
};
}
const double = multiply(2); // 创建一个函数,固定第一个参数为 2
const triple = multiply(3); // 创建一个函数,固定第一个参数为 3
console.log(double(4)); // 8
console.log(triple(4)); // 12
范例 5:事件处理中的闭包
闭包可以让事件处理器记住它绑定时的上下文。
function setupButtons() {
for (let i = 1; i <= 3; i++) {
document.getElementById(`button${i}`).addEventListener("click", function () {
console.log(`Button ${i} clicked!`);
});
}
}
在这里,闭包使得每个事件处理器记住了绑定时的 i 值。
注意事项
总结
闭包适合用于:
- 封装私有数据
- 保存状态
- 创建灵活的工厂函数或柯里化
- 处理异步操作和事件绑定闭包是 JavaScript 中强大而灵活的特性,适用于需要「记住某些信息」的场景。