1. 概述

闭包(Closure)是现代编程语言中非常强大且常用的一个特性。它与作用域(Scope)和作用域链(Scope Chain)密切相关。本文将从变量作用域讲起,逐步深入理解闭包的本质和应用场景。

2. 变量作用域

变量作用域决定了变量在程序中的可见性和生命周期。在大多数语言中,变量作用域分为全局作用域和局部作用域。

2.1 全局作用域

在函数外部声明的变量属于全局作用域,可以在程序的任何地方访问:

var foo = 1;

function bar() {
    console.log(foo);
}

console.log(foo); // 输出 1
bar(); // 输出 1

2.2 局部作用域 / 函数作用域

在函数内部声明的变量只能在该函数内部访问,外部无法访问:

function bar() {
    var foo = 1;
    console.log(foo); // 输出 1
}

console.log(foo); // 报错:foo is not defined
bar(); // 输出 1

3. 闭包的概念

闭包的核心在于:函数可以访问并记住其词法作用域,即使该函数在其作用域外执行

当一个函数返回另一个函数时,返回的函数会保留对其父函数作用域中变量的引用。这个引用集合就被称为闭包。

举个例子:

function foo() {
    var a = 10; // 外部作用域变量

    return function bar() {
        var b = 10; // 内部作用域变量
        console.log(a + b); 
    }
}

var result = foo();
result(); // 输出 20

再看一个更典型的例子:

function foo(param) {
    return function bar() {
        var b = 10;
        console.log(param + b); 
    }
}

var anotherWayToCall = foo(10); // 返回函数
anotherWayToCall(); // 输出 20

关键点:闭包会“记住”它被创建时的环境,包括所有外部变量的引用。即使外层函数已经执行完毕,这些变量也不会被垃圾回收。

⚠️ 注意:滥用闭包可能导致内存泄漏,特别是在 DOM 元素绑定事件或大量缓存时。

4. 闭包的实际应用场景

闭包在实际开发中有很多用途,下面是一些常见的使用场景:

4.1 封装私有变量

闭包可以用来模拟类的私有属性,避免全局变量污染:

function Counter() {
    var count = 0;
    return {
        increment: function() { count++; },
        getCount: function() { return count; }
    };
}

var counter = Counter();
counter.increment();
console.log(counter.getCount()); // 输出 1

4.2 回调函数与事件处理

闭包常用于事件监听或异步编程中,保留上下文状态:

function setupButton() {
    var count = 0;
    document.getElementById('myButton').addEventListener('click', function() {
        count++;
        console.log('按钮被点击了 ' + count + ' 次');
    });
}

4.3 函数工厂

闭包可以动态生成具有不同配置的函数:

function createMultiplier(factor) {
    return function(number) {
        return number * factor;
    };
}

var double = createMultiplier(2);
var triple = createMultiplier(3);

console.log(double(5)); // 输出 10
console.log(triple(5)); // 输出 15

5. 总结

闭包是一个函数与其词法作用域的组合,它能够记住并访问其创建时的环境变量。它是 JavaScript 等语言中实现封装、模块化、函数式编程等高级特性的基础。

掌握闭包有助于写出更优雅、模块化的代码,也是前端面试中高频考点之一。理解闭包的原理和作用域链的机制,能帮助我们避免一些常见的内存泄漏和作用域污染问题。

如果你在开发中遇到变量被“意外保留”的情况,很可能就是闭包在起作用,记得检查引用链。闭包虽强大,但也要合理使用。


原始标题:What Is a Closure – Different Types of Scopes

« 上一篇: 代码覆盖率