跳到主要内容

闭包和词法作用域

在 JavaScript 中,闭包(Closure) 是指一个函数能够访问并记住其词法作用域(Lexical Scope)中的变量,即使这个函数在其原始作用域之外被调用

简单来说:

闭包 = 函数 + 它创建时所处的环境(外部作用域的变量)


一、直观例子

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

function inner() {
count++; // inner 访问了 outer 的变量 count
console.log(count);
}

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

const fn = outer(); // outer 执行完毕,按理说 count 应该被销毁
fn(); // 输出 1
fn(); // 输出 2
  • 虽然 outer() 已经执行结束,但 inner 依然能“记住”并操作 count
  • 这就是闭包:inner 捕获了 outer 的作用域。

二、闭包形成的关键条件

  1. 嵌套函数:内部函数定义在外部函数中;
  2. 内部函数引用了外部函数的变量
  3. 外部函数返回了内部函数(或以某种方式将其暴露到外部)

只要满足这三点,闭包就形成了。


三、闭包的本质:作用域链的保留

JavaScript 的函数在创建时,会绑定其所在的词法环境(Lexical Environment)。
即使外部函数执行完毕,只要内部函数还存在(未被垃圾回收),它所依赖的外部变量就不会被释放。

🧠 可以理解为:闭包让函数“随身携带”了一个小背包,里面装着它出生时周围的所有变量。


四、总结

特性说明
是什么函数 + 其创建时的词法环境
核心能力让函数访问“已经退出”的作用域中的变量
优点实现数据封装、状态保持、高阶函数等
风险可能导致内存泄漏、意外的变量共享

💡 一句话理解闭包
函数记住了它出生的地方,并随时可以回去拿东西。

闭包是 JavaScript 最强大也最易被误解的特性之一,掌握它,你就真正理解了 JS 的作用域和函数本质。


词法作用域

词法作用域(Lexical Scope) 是 JavaScript(以及其他许多编程语言)中变量查找规则的核心机制。它的核心思想是:

作用域在代码书写时(“词法阶段”)就已经确定了,与运行时的调用位置无关。


一、“词法”是什么意思?

  • “词法”(Lexical)指的是源代码的书写结构
  • 引擎在解析代码时(还没执行),就根据代码的嵌套结构静态地确定了每个函数的作用域链。

✅ 所以词法作用域也叫 静态作用域(Static Scope)


二、对比:动态作用域(Dynamic Scope)

为了更清楚,我们对比一下动态作用域(JavaScript 不使用这种):

// 假设 JS 是动态作用域(实际不是!)
let x = 1;

function foo() {
console.log(x); // 动态作用域下,看谁调用了我
}

function bar() {
let x = 2;
foo(); // 如果是动态作用域,这里会输出 2
}

bar(); // 实际 JS 输出 1(因为是词法作用域)
  • 动态作用域:函数执行时,沿着调用栈向上找变量。
  • 词法作用域:函数执行时,沿着定义时的嵌套结构(作用域链)找变量。

🌟 JavaScript 是词法作用域,不是动态作用域。


三、常见误区澄清

❌ 误区:“this 的值由词法作用域决定”

  • 错误! this动态绑定的,由调用方式决定(如 obj.method()this === obj)。
  • 词法作用域只管变量查找(如 x, y),不管 this

✅ 正确:箭头函数的 this 是词法的

  • 箭头函数没有自己的 this,它会继承外层作用域的 this —— 这里的“外层作用域”是词法上的
  • 所以箭头函数的 this 表现得像“词法变量”。