Z
ZHANK
函数

作用域与闭包

理解词法作用域、闭包原理与实战

作用域与闭包

闭包是 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