闭包应用理解

202 阅读2分钟

闭包(Closure) 是指一个 函数能够访问其外部作用域(函数外部)的变量,即使在外部函数执行结束后,变量仍然可以被访问

闭包的原理

在 JavaScript 中,每当创建一个函数时,就会同时创建一个闭包。闭包使得内部函数可以“记住”其创建时的词法作用域(Lexical Scope) ,即使外部函数已经执行完毕并返回,内部函数依然可以访问外部函数的变量。


示例:经典闭包

function outerFunction() {
  let count = 0; // 外部变量return function innerFunction() {
    count++; // 访问外部作用域的变量
    console.log(count);
  };
}
​
const counter = outerFunction(); // 返回一个内部函数
counter(); // 输出:1
counter(); // 输出:2
counter(); // 输出:3

分析

  1. outerFunction() 执行后返回 innerFunction(),但 count 变量不会被销毁,因为 innerFunction 仍然持有对 count 的引用。
  2. counter() 每次执行,都会增加 count 的值并打印出来,即使 outerFunction 已经执行完毕。
  3. 这种行为就是闭包函数记住并访问了创建它的作用域(count 变量)

实际应用场景

1. 数据私有化

闭包可以用于创建私有变量,避免变量污染全局作用域:

function createCounter() {
  let count = 0; // 私有变量return {
    increment: function () {
      count++;
      console.log(count);
    },
    decrement: function () {
      count--;
      console.log(count);
    },
    getCount: function () {
      return count;
    }
  };
}
​
const counter = createCounter();
counter.increment(); // 输出:1
counter.increment(); // 输出:2
console.log(counter.getCount()); // 输出:2
console.log(counter.count); // undefined,外部无法直接访问

优点

  • count 变量被封装在 createCounter() 内部,外部无法直接修改它,只能通过 incrementdecrement 操作。

2. 延迟执行(定时器)

function delayedMessage(message, delay) {
  setTimeout(() => {
    console.log(message);
  }, delay);
}
​
delayedMessage("Hello, world!", 2000); // 2秒后输出 "Hello, world!"

为什么是闭包?

  • setTimeout 的回调函数是在 delayedMessage 执行完毕后才运行的,但它仍然能访问 message 变量。
  • 在适当的时候清除定时器clearInterval防止内存泄漏

3. 事件监听器

function attachEventListeners() {
  let count = 0; // 闭包变量document.getElementById("btn").addEventListener("click", function () {
    count++;
    console.log(`按钮点击次数: ${count}`);
  });
}
​
attachEventListeners();

为什么是闭包?

  • click 事件的回调函数仍然可以访问 count 变量,即使 attachEventListeners 已经执行完毕。
  • 防止内存泄露,在不再需要时,移除事件监听器removeEventListener

4. 柯里化(Currying)

function multiply(a) {
  return function (b) {
    return a * b;
  };
}
​
const double = multiply(2);
console.log(double(5)); // 输出:10
console.log(double(10)); // 输出:20

为什么是闭包?

  • multiply(2) 返回的函数仍然可以访问 a = 2,即使 multiply 已经执行完毕。

总结

闭包的特点

  1. 内部函数可以访问外部函数的变量,即使外部函数已经执行结束
  2. 变量不会被垃圾回收,因为它们仍然被内部函数引用
  3. 适用于数据私有化、定时器、事件监听、柯里化等场景

闭包既是 JavaScript 强大的特性,也是导致内存泄漏的潜在风险,因此要合理使用闭包,避免不必要的变量占用内存。