函数
作用域与闭包
理解词法作用域、闭包原理与实战
作用域与闭包
闭包是 JavaScript 最强大的特性之一,也是面试必考知识点。
学完本章你将: 理解词法作用域、闭包原理与实战应用。
作用域类型
javascript
// 1. 全局作用域
let globalVar = "全局";
// 2. 函数作用域
function foo() {
let funcVar = "函数内";
console.log(globalVar); // ✅ 可访问
}
// console.log(funcVar); // ❌ 不可访问
// 3. 块作用域(let/const)
if (true) {
let blockVar = "块内";
console.log(blockVar); // ✅
}
// console.log(blockVar); // ❌
词法作用域
javascript
// 内部函数可以访问外部函数的变量
function outer() {
let outerVar = "外部";
function inner() {
console.log(outerVar); // ✅ 可以访问
}
inner();
}
outer(); // "外部"
// 作用域链
let global = "global";
function a() {
let aVar = "a";
function b() {
let bVar = "b";
function c() {
console.log(global, aVar, bVar); // 都能访问!
}
c();
}
b();
}
a(); // "global a b"
闭包(Closure)
闭包 = 函数 + 其引用的外部变量
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
// 每次调用 createCounter 创建独立作用域
const c1 = createCounter();
const c2 = createCounter();
console.log(c1()); // 1
console.log(c2()); // 1(独立)
闭包实战
javascript
// 1. 数据私有化
function createBank(initialBalance) {
let balance = initialBalance;
return {
deposit(amount) { balance += amount; return balance; },
withdraw(amount) { balance -= amount; return balance; },
getBalance() { return balance; },
};
}
const account = createBank(1000);
account.deposit(500);
console.log(account.getBalance()); // 1500
// account.balance; // undefined(外部无法直接访问)
// 2. 循环中的经典问题
for (var i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), i * 100); // 4 4 4!
}
// 修复方式 1:let(块作用域)
for (let j = 1; j <= 3; j++) {
setTimeout(() => console.log(j), j * 100); // 1 2 3 ✅
}
// 修复方式 2:闭包
for (var k = 1; k <= 3; k++) {
(function(n) {
setTimeout(() => console.log(n), n * 100);
})(k); // 1 2 3 ✅
}
闭包的代价
闭包会阻止垃圾回收,可能导致内存泄漏:
javascript
// 不要滥用闭包存储大量数据
// 使用完及时解除引用
let release = null;
function heavy() {
let bigData = new Array(1000000);
return function() { return bigData; };
}
release = heavy(); // bigData 无法被回收
release = null; // 解除引用,允许 GC