Jonvy

导航

JavaScript 闭包(Closures)详解

1. 闭包是什么?

闭包(Closure) 是指一个函数能够访问并记住其词法作用域(Lexical Scope),即使该函数在其定义的作用域之外执行。简单来说:

  • 函数 + 它所在的词法环境 = 闭包。

  • 闭包允许函数访问它被创建时的作用域链,即使该函数在其他地方被调用。


2. 闭包的核心特点

  1. 函数嵌套函数(内部函数引用外部函数的变量)。

  2. 外部函数执行完毕后,内部函数仍能访问其变量(变量不会被垃圾回收)。


3. 闭包示例

示例 1:基本闭包

javascript
 
function outer() {
  let count = 0; // 外部函数的变量

  function inner() {
    count++; // 内部函数访问外部变量
    console.log(count);
  }

  return inner; // 返回内部函数
}

const counter = outer(); // outer() 执行完毕,但 count 仍被 inner 记住
counter(); // 输出 1
counter(); // 输出 2(count 保持状态)

解释:

  • outer() 执行后,count 变量本应被销毁,但由于 inner() 引用了它,闭包保留了 count

  • counter() 每次调用都会修改并记住 count 的值。


示例 2:闭包实现私有变量

javascript
 
function createCounter() {
  let privateCount = 0; // 私有变量,外部无法直接访问

  return {
    increment: function() {
      privateCount++;
    },
    getCount: function() {
      return privateCount;
    }
  };
}

const counter = createCounter();
counter.increment();
console.log(counter.getCount()); // 输出 1
console.log(counter.privateCount); // undefined(无法直接访问)

解释:

  • privateCount 只能通过闭包提供的 increment() 和 getCount() 方法访问,实现了数据封装。


示例 3:循环中的闭包(经典面试题)

javascript
 
for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 输出 3, 3, 3(不是预期的 0, 1, 2)
  }, 1000);
}

问题:由于 var 没有块级作用域,setTimeout 回调执行时 i 已经是 3。
解决方案(用闭包或 let):

javascript
 
// 方法 1:使用 IIFE(立即执行函数)创建闭包
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(function() {
      console.log(j); // 输出 0, 1, 2
    }, 1000);
  })(i);
}

// 方法 2:使用 let(块级作用域)
for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i); // 输出 0, 1, 2
  }, 1000);
}

4. 闭包的优缺点

优点

  1. 数据私有化:隐藏变量,避免全局污染(如示例 2)。

  2. 保持状态:函数执行后,变量仍可被访问(如计数器示例)。

  3. 模块化开发:早期 JS 用闭包模拟模块(现代用 ES6 import/export)。

缺点

  1. 内存泄漏:闭包会长期占用内存(变量未被释放),滥用可能导致性能问题。

  2. 调试困难:闭包的作用域链较复杂,可能增加代码理解难度。


5. 闭包的应用场景

  1. 防抖(Debounce)和节流(Throttle)

    javascript
     
    function debounce(fn, delay) {
      let timer;
      return function() {
        clearTimeout(timer);
        timer = setTimeout(() => fn.apply(this, arguments), delay);
      };
    }
  2. 柯里化(Currying)

    javascript
     
    function add(a) {
      return function(b) {
        return a + b;
      };
    }
    const add5 = add(5);
    console.log(add5(3)); // 8
  3. React Hooks(如 useState
    React 的 Hooks 机制依赖闭包来记住状态。


6. 如何判断闭包?

  • 如果一个函数访问了它外部的变量,并且该函数在外部作用域之外被调用,那么这就是闭包。


总结

  • 闭包的本质:函数 + 其词法作用域。

  • 核心作用:让函数“记住”它定义时的环境(变量)。

  • 使用场景:私有变量、模块化、高阶函数(如防抖)、React Hooks 等。

  • 注意事项:避免滥用,防止内存泄漏。

闭包是 JavaScript 的核心概念,理解它才能写出更高级的代码! 🚀

posted on 2025-08-05 18:02  不亮  阅读(48)  评论(0)    收藏  举报