闭包:函数嵌套函数,内部函数访问外部作用域,变量永不释放!
function createCounter() {
let count = 0; // ? 被记住的变量
return function() {
count++; // ? 闭包诞生!
return count;
};
}
以下是三个闭包实战场景的代码实现
1. 防抖按钮(连续点击只触发一次)
// 防抖函数:连续点击时只在最后一次执行
function debounce(fn, delay) {
let timer = null; // ? 闭包保存的定时器
return function() {
clearTimeout(timer); // 清除上次的定时器
timer = setTimeout(() => {
fn.apply(this, arguments); // ✅ 延迟执行目标函数
}, delay);
};
}
// 使用示例
const searchButton = document.getElementById('search-btn');
searchButton.addEventListener('click', debounce(() => {
console.log('搜索请求已发送!'); // ? 实际业务请求
}, 500));
2. 私有变量(外部无法修改内部数据)
function createBankAccount(initialBalance) {
let balance = initialBalance; // ? 闭包保护的私有变量
return {
deposit: (amount) => {
balance += amount;
console.log(`存款成功,余额:${balance}`);
},
withdraw: (amount) => {
if (amount > balance) {
console.log("余额不足!");
return;
}
balance -= amount;
console.log(`取款成功,余额:${balance}`);
},
getBalance: () => {
return balance; // ? 外部只能通过方法访问
}
};
}
// 使用示例
const myAccount = createBankAccount(1000);
myAccount.deposit(500); // ✅ 存款成功,余额:1500
myAccount.withdraw(200); // ✅ 取款成功,余额:1300
console.log(myAccount.balance); // ❌ undefined (无法直接访问)
console.log(myAccount.getBalance()); // ✅ 1300 (安全访问)
3. 循环陷阱修复(setTimeout正确输出索引)
// 错误版本:输出全是5
for (var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // ❌ 输出:5,5,5,5,5
}, 100);
}
// 闭包修复版本:正确输出0-4
for (var i = 0; i < 5; i++) {
(function(j) { // ? IIFE创建闭包作用域
setTimeout(() => {
console.log(j); // ✅ 输出:0,1,2,3,4
}, 100);
})(i); // 立即传入当前i值
}
// 现代方案:使用let块级作用域
for (let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i); // ✅ 输出:0,1,2,3,4
}, 100);
}