首页
/ 深入理解闭包:从whatthefuck.is项目看JavaScript闭包本质

深入理解闭包:从whatthefuck.is项目看JavaScript闭包本质

2025-07-10 03:49:17作者:邵娇湘

闭包(Closure)是JavaScript中一个既强大又容易让人困惑的概念。许多开发者在不知不觉中已经使用了闭包,但却没有真正理解它的工作原理。本文将从基础开始,逐步揭示闭包的奥秘。

什么是闭包?

简单来说,当一个函数访问了定义在它外部的变量时,就形成了闭包。让我们看一个例子:

let users = ['Alice', 'Dan', 'Jessica'];
let query = 'A';
let user = users.filter(user => user.startsWith(query));

在这个例子中,箭头函数user => user.startsWith(query)访问了外部定义的query变量,这就是一个闭包。

为什么闭包难以理解?

闭包之所以让人困惑,主要有两个原因:

  1. 它是一个"隐形"的概念:开发者经常在不知道的情况下使用闭包
  2. 它打破了我们对变量生命周期的常规理解:闭包使得函数可以"记住"创建时的环境

闭包的发现之旅

让我们通过三个步骤来真正理解闭包:

第一步:函数可以访问外部变量

JavaScript中的函数可以访问定义在它们外部的变量:

let food = 'cheese'; // 外部变量

function eat() {
  console.log(food + ' is good'); // 访问外部变量
}

eat(); // 输出 "cheese is good"
food = 'pizza';
eat(); // 输出 "pizza is good"

这个特性是闭包的基础之一。

第二步:用函数包装代码

我们可以把任意代码包装在函数中,然后调用这个函数:

function doTheThing() {
  // 任意代码
}

doTheThing();

关键点在于:把代码包装在函数中并调用一次,不会改变代码的行为

第三步:闭包的诞生

结合前两步,我们来看这个例子:

function liveADay() {
  let food = 'cheese'; // 外部变量
  
  function eat() {
    console.log(food + ' is good'); // 内部函数访问外部变量
  }
  
  eat();
}

liveADay();

这里,eat函数访问了定义在liveADay函数中的food变量,这就是闭包。

闭包的深层理解

闭包不仅仅是函数访问外部变量这么简单,它还有一些重要的特性:

1. 变量捕获

闭包会"捕获"它引用的外部变量,即使外部函数已经执行完毕:

function liveADay() {
  let food = 'cheese';
  
  function eat() {
    console.log(food + ' is good');
  }
  
  setTimeout(eat, 5000); // 5秒后调用
}

liveADay(); // liveADay执行完毕,但food变量仍然存在

在这个例子中,即使liveADay函数已经执行完毕,food变量仍然被保留,直到eat函数被调用。

2. 每次调用创建新的闭包

每次外部函数被调用时,都会创建一个新的闭包环境:

function makeCounter() {
  let count = 0;
  return function() {
    return count++;
  };
}

let counter1 = makeCounter();
let counter2 = makeCounter();

console.log(counter1()); // 0
console.log(counter1()); // 1
console.log(counter2()); // 0 (独立的闭包环境)

3. 闭包的实际应用

闭包在JavaScript中有广泛的应用:

  • 数据封装:创建私有变量
  • 函数工厂:生成特定配置的函数
  • 事件处理:保持对事件上下文的访问
  • 模块模式:实现模块化的代码组织

为什么叫"闭包"?

"闭包"这个术语来自计算机科学。在数学和编程语言理论中,当一个表达式包含自由变量(如user => user.startsWith(query)中的query),我们说这个表达式有"开放绑定"。当这个表达式能够访问外部作用域中的变量时,我们就"关闭"了这个开放绑定,因此称为"闭包"。

闭包与其他语言

不是所有语言都支持闭包:

  • C语言:不允许嵌套函数,无法形成闭包
  • Rust:有专门的闭包语法,与普通函数区分
  • JavaScript:完全支持闭包,是语言的核心特性之一

总结

闭包是JavaScript中一个强大而优雅的特性。理解闭包的关键点:

  1. 闭包发生在函数访问外部变量时
  2. 闭包会"记住"它的创建环境
  3. 闭包使得变量可以在函数执行完毕后继续存在
  4. 每次外部函数调用都会创建新的闭包环境

掌握了闭包,你就掌握了JavaScript中许多高级模式的基础。希望这篇文章能帮助你真正理解闭包的本质和应用。