闭包在JavaScript中非常实用,常见的使用场景包括:
1. 数据封装与私有变量
创建只能通过特定方法访问的私有数据:
function createCounter() {
let count = 0; // 私有变量
return {
increment: function() {
count++;
return count;
},
decrement: function() {
count--;
return count;
},
getCount: function() {
return count;
}
};
}
const counter = createCounter();
console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.count); // undefined,无法直接访问
2. 函数工厂
动态创建具有特定配置的函数:
function createMultiplier(multiplier) {
return function(x) {
return x * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
3. 事件处理与回调函数
保持对事件上下文的引用:
function setupButton() {
let clickCount = 0;
document.getElementById('myButton').addEventListener('click', function() {
clickCount++;
console.log(`按钮被点击了 ${clickCount} 次`);
});
}
// 每次点击都会记住并更新 clickCount
4. 模块模式
实现模块化代码,暴露公共接口,隐藏实现细节:
const calculator = (function() {
let memory = 0;
function add(x, y) {
return x + y;
}
function store(value) {
memory = value;
}
return {
add: add,
store: store,
getMemory: function() {
return memory;
}
};
})();
console.log(calculator.add(2, 3)); // 5
calculator.store(10);
console.log(calculator.getMemory()); // 10
5. 防抖与节流
优化高频触发的事件:
// 防抖:等待一段时间后才执行
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 节流:固定时间执行一次
function throttle(fn, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
6. 记忆化/缓存函数
缓存计算结果,提高性能:
function memoize(fn) {
const cache = {};
return function(...args) {
const key = JSON.stringify(args);
if (cache[key] === undefined) {
cache[key] = fn.apply(this, args);
}
return cache[key];
};
}
const memoizedFactorial = memoize(function(n) {
if (n === 0) return 1;
return n * memoizedFactorial(n - 1);
});
7. 循环中的闭包
解决异步循环中的变量捕获问题:
// 常见问题
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 全部输出5
}, 100);
}
// 解决方案1:使用IIFE
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 0,1,2,3,4
}, 100);
})(i);
}
// 解决方案2:使用let(块级作用域)
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 0,1,2,3,4
}, 100);
}
8. 函数柯里化
将多参数函数转换为一系列单参数函数:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
}
};
}
function add(a, b, c) {
return a + b + c;
}
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
9. 状态管理
在函数式编程中管理状态:
function createState(initialState) {
let state = initialState;
return {
getState: () => state,
setState: (newState) => {
state = typeof newState === 'function'
? newState(state)
: newState;
}
};
}
注意事项
- 内存泄漏:闭包会阻止垃圾回收,注意解除不必要的引用
- 性能考量:过度使用闭包可能影响性能
- 调试难度:闭包可能使调试更复杂
闭包是JavaScript强大功能的体现,合理使用可以写出更优雅、模块化的代码。