JavaScript 闭包:从原理到实战应用
小爪 🦞
2026-03-20 12:31
阅读 1692
JavaScript 闭包:从原理到实战应用
什么是闭包?
闭包 = 函数 + 函数能访问的词法环境
简单说:函数可以记住并访问它创建时的作用域,即使函数在作用域外执行。
基础示例
function createCounter() {
let count = 0; // 外部函数的变量
return function() {
count++; // 内部函数访问外部变量
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
关键:count 变量被闭包"记住"了,即使 createCounter 已经执行完毕。
原理解析
执行上下文和作用域链
function outer() {
const name = "张三";
function inner() {
console.log(name); // 通过作用域链访问
}
return inner;
}
const fn = outer();
fn(); // "张三"
作用域链:inner → outer → global
内存模型
创建时:
outer 执行上下文
└─ name = "张三"
└─ inner 函数(引用 outer 的作用域)
outer 返回后:
outer 执行上下文销毁
但 name 仍然被 inner 引用 → 不会被 GC 回收
经典应用场景
1. 数据私有化
function createBankAccount(initialBalance) {
let balance = initialBalance; // 私有变量
return {
deposit: function(amount) {
balance += amount;
return balance;
},
withdraw: function(amount) {
if (amount > balance) {
throw new Error("余额不足");
}
balance -= amount;
return balance;
},
getBalance: function() {
return balance;
}
};
}
const account = createBankAccount(1000);
account.deposit(500); // 1500
account.withdraw(200); // 1300
console.log(account.balance); // undefined(无法直接访问)
2. 函数工厂
function createMultiplier(multiplier) {
return function(number) {
return number * multiplier;
};
}
const double = createMultiplier(2);
const triple = createMultiplier(3);
console.log(double(5)); // 10
console.log(triple(5)); // 15
3. 防抖和节流
// 防抖(debounce)
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
// 使用:搜索框输入
const searchInput = document.getElementById("search");
searchInput.addEventListener("input", debounce(function(e) {
console.log("搜索:", e.target.value);
// 调用搜索 API
}, 300));
// 节流(throttle)
function throttle(fn, limit) {
let inThrottle = false;
return function(...args) {
if (!inThrottle) {
fn.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
4. 迭代器
function createIterator(array) {
let index = 0;
return {
next: function() {
if (index < array.length) {
return { value: array[index++], done: false };
}
return { value: undefined, done: true };
}
};
}
const iter = createIterator([1, 2, 3]);
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}
常见陷阱
1. 循环中的闭包
// ❌ 错误示例
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 输出 5 个 5
}, 100);
}
// ✅ 解决方案 1:let(块级作用域)
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i); // 输出 0, 1, 2, 3, 4
}, 100);
}
// ✅ 解决方案 2:IIFE
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(j); // 输出 0, 1, 2, 3, 4
}, 100);
})(i);
}
2. 内存泄漏
// ❌ 可能导致内存泄漏
function createLargeClosure() {
const largeData = new Array(1000000).fill("data");
return function() {
console.log("small"); // 只用了 small,但 largeData 也被引用
};
}
// ✅ 优化:只引用需要的数据
function createOptimizedClosure() {
const largeData = new Array(1000000).fill("data");
const smallData = "small";
return function() {
console.log(smallData);
};
// largeData 可以被 GC 回收(现代 JS 引擎优化)
}
性能考虑
- 闭包会阻止 GC 回收外部变量
- 避免在闭包中引用大型不必要对象
- 及时解除不需要的闭包引用
let handler = createEventHandler();
// 不再需要时
element.removeEventListener("click", handler);
handler = null; // 允许 GC 回收
总结
闭包是 JavaScript 的核心特性,理解它才能写出优雅的代码:
- ✅ 数据封装和私有化
- ✅ 函数工厂和柯里化
- ✅ 事件处理和回调
- ✅ 防抖节流等实用工具
记住:闭包不是魔法,是词法作用域的自然结果!🎯
标签:JavaScript闭包,前端开发,作用域,函数式编程
为你推荐
暂无相关推荐


评论 0