编程 JavaScript 闭包:函数、词法作用域与记住的环境

2026-09-08 00:14:58

JavaScript 闭包:函数、词法作用域与"记住"的环境

闭包是 JavaScript 最重要的概念之一,初看让人困惑,因为它同时涉及函数、词法作用域和词法环境。三者连起来之后,闭包就简单了。

定义:闭包是一个函数,即使外层函数已经执行完毕,它仍记得并能访问其周边词法环境里的变量。

"记得"不是说 JavaScript 把变量复制进函数——而是函数保持与创建它的词法环境的连接

一个例子讲透

function outer() {
  let name = "Abimanyu"
  function inner() {
    console.log(name)
  }
  return inner
}

let myFunction = outer()
myFunction()

outer() 被调用时创建了包含 name 的词法环境;inner() 创建在 outer() 内部,所以能访问周边环境。outer() 返回 inner 后,outer() 已经执行完——但 myFunction 仍引用 inner,而 inner 仍引用那个环境。调用 myFunction() 时,name 不在 inner 自己的环境里,JavaScript 沿周边环境链找到它。

为什么 name 没有消失? 你可能以为 outer() 结束后它内部的东西都该消失。但 inner() 还引用着包含 name 的环境,这个环境通过函数仍然可达,JavaScript 不能回收它。

闭包与词法作用域

词法作用域意味着函数的作用域由它写在哪决定,不是在哪被调用inner() 写在 outer() 内部,所以它的周边词法作用域包含 outer()。这个关系在调用之前就存在——函数在源码里的位置决定了它的词法周边。

闭包的两个实用价值

保持状态——函数在多次调用间维持状态:

function createCounter() {
  let count = 0
  return function() {
    count++
    return count
  }
}
let counter = createCounter()
counter() // 1
counter() // 2
counter() // 3

每次调用访问的是同一个 count 变量并修改它,值不会每次从零重建——闭包保持与创建它的环境的连接。

数据私有——让变量不被外部代码直接访问:

function createAccount() {
  let balance = 1000
  return {
    deposit(amount) { balance += amount },
    getBalance() { return balance }
  }
}
let account = createAccount()
account.deposit(500)
account.getBalance() // 1500

balance 无法通过 account.balance 直接访问,但 depositgetBalance 创建在包含 balance 的环境里,可以访问它。闭包让函数操作私有状态而不暴露变量本身。

实践建议

  • 闭包的关键句:函数保持与创建它的环境的连接,而非复制变量;
  • 用闭包做"私有状态"(计数器、账户余额、模块内部变量),比挂全局干净得多;
  • 面试与代码评审都爱考:闭包是理解事件回调、定时器、循环陷阱(var 时代的经典)的地基。

来源:Closure in JavaScript - DEV Community

复制全文 生成海报 JavaScript 教程

推荐文章

程序员茄子在线接单