1. 概念

闭包的核心是词法作用域,即函数在定义时所处的作用域,而不是在执行时所处的作用域。这意味着,函数可以访问定义时所处的作用域中的变量,即使这个函数在其他地方被调用。

2. 经典示例

1
2
3
4
5
6
7
8
9
function outerFun(){
let n = 1
function innerFun(){
console.log(n)
}
return innerFun
}
const newFun = outerFun()
newFun() //输出 1

在这个例子中:

  1. outerFun定义了一个局部变量n和一个内部函数innerFun
  2. innerFun访问了outerFun,即使outerFun已经执行完毕。
  3. outerFun返回了innerFun,并将其复制给newFun
  4. newFun被调用时,它仍然可以访问n,这就是闭包的作用。

闭包的应用场景

  1. 数据封装和私有变量:闭包可以用来创建私有变量,防止外部直接访问和修改。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function createCounter() {
    let count = 0;
    return function() {
    count++;
    return count;
    };
    }

    const counter = createCounter();
    console.log(counter()); // 输出: 1
    console.log(counter()); // 输出: 2
    在这个例子中,count 变量被封装在 createCounter 函数内部,外部无法直接访问或修改它,只能通过返回的闭包函数来操作。
  2. 回调函数和事件处理:闭包常用于回调函数和事件处理程序中,以便函数执行时访问定义时的上下文。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function setupButton() {
    let count = 0;
    document.getElementById('myButton').onclick = function() {
    count++;
    console.log(`Button clicked ${count} times`);
    };
    }

    setupButton();
    在这个例子中,点击按钮时,回调函数可以访问count变量,即使setupButton函数已经执行完毕。

闭包的注意事项

  1. 内存泄漏:由于闭包会保留对其词法作用域的引用,可能会导致内存泄漏,尤其在使用不当的情况下。如在不需要闭包时,仍然保留对它的引用,可能会导致相关变量无法被回收。
  2. 性能问题:闭包可能会带来一些性能开销,因为他们需要维护额外的作用域链。

总结

闭包是 JavaScript 中一个强大且常用的特性,它允许函数访问其词法作用域中的变量,即使函数在其作用域之外执行。闭包在数据封装、回调函数、柯里化等场景中非常有用,但也需要注意内存泄漏和性能问题。