简单了解 JavaScript 闭包

闭包是 JavaScript 中一个非常重要的,而且是其中最具特色的概念之一。让我们一起了解一下什么是闭包以及它为什么如此重要。

什么是闭包?

根据 MDN 对于闭包的解释:

闭包 (closure)是一个函数以及其捆绑的周边环境状态(lexical environment,词法环境)的引用的组合。换而言之,闭包让开发者可以从内部函数访问外部函数的作用域。在 JavaScript 中,闭包会随着函数的创建而被同时创建。

闭包的实质是一个函数可以访问其外部作用域的变量,即使这个函数在其外部作用域已经退出。这种特性使得闭包能够在函数调用之间保留状态,实现跨函数的数据传递。

javascript 复制代码
function outer() {
  var outerVar = '外部作用域的变量'

  function inner() {
    console.log(outerVar) // inner 函数可以访问 outer 函数中的变量
  }

  return inner
}

var closureFunc = outer()
closureFunc() // 输出 "外部作用域的变量"

在上面的例子中,inner 函数形成了一个闭包,它可以访问 outer 函数中的 outerVar 变量,即使 outer 函数已经执行完毕并且 outer 函数的执行上下文已经出栈。这是因为 inner 函数继续保持对 outer 函数作用域的引用,这就是闭包的本质。

闭包的工作原理

作用域链(Scope Chain)

要理解闭包的工作原理,需要了解 JavaScript 的作用域链机制。

JavaScript 中的作用域链是一个从函数内部到外部作用域的链式结构。当一个函数内部引用外部变量时,JavaScript 引擎会从内部函数开始,沿着作用域链向上查找,直到找到该变量的定义为止。作用域链的查找顺序是:首先查找函数内部的局部变量,然后是其外部祖先函数的局部变量,最后是全局作用域。

变量提升(Variable Hoisting)

JavaScript 中,变量提升是指变量在声明后,其作用域会向上提升至最近的函数外部作用域。这意味着即使在一个函数内部声明了一个外部变量,JavaScript 引擎也会在函数外部为其创建一个全局变量。因此,当内部函数引用了外部变量时,实际上是在引用全局变量。

闭包的实现

闭包的实现依赖于作用域链和变量提升。当一个内部函数引用了外部变量时,JavaScript 引擎会在内部函数中创建一个对该外部变量的引用。当内部函数执行完毕,其作用域消失,但内部函数对外部变量的引用仍然存在。此时,如果外部函数继续执行,其作用域链中的外部变量会被提升,从而使得内部函数引用的外部变量得以保留。

闭包的实际应用

封装数据

一个常见的用途是通过闭包创建私有变量,以实现数据的封装和保护。这有助于防止其他代码意外地修改或访问数据。

javascript 复制代码
function createCounter() {
  var count = 0

  return {
    increment: function () {
      count++
    },
    decrement: function () {
      count--
    },
    getValue: function () {
      return count
    },
  }
}

var counter = createCounter()
counter.increment()
counter.increment()
console.log(counter.getValue()) // 输出 2

在这个示例中,count 变量是私有的,只能通过返回的对象中的方法进行访问和修改,从而确保数据的封装性和隐私性。

延迟执行

闭包还可以用于创建延迟执行的函数,例如在事件处理程序中。

javascript 复制代码
function createDelayedFunction(message, delay) {
  return function () {
    setTimeout(function () {
      console.log(message)
    }, delay)
  }
}

var delayedFunc = createDelayedFunction('延迟消息', 2000)
delayedFunc() // 2 秒后输出 "延迟消息"

在这里,createDelayedFunction 函数返回一个闭包,它可以延迟执行一段代码,以便在指定的时间后输出消息。

参考资料

相关推荐
布朗克1682 小时前
39 Spring Boot Web实战
前端·spring boot·后端·实战
纽格立科技2 小时前
DRM 发射端链路图(上)
前端·人工智能·车载系统·信息与通信·传媒
云水一下2 小时前
Vue.js从零到精通系列(七):高级特性实战——Teleport、异步组件、自定义指令与TypeScript深度结合
前端·vue.js·typescript
qq4356947013 小时前
Vue05
前端·vue.js
qq_422152573 小时前
PDF 解密工具怎么选?2026 年文档密码移除方案与注意事项
java·前端·pdf
YHHLAI3 小时前
前端工程化调用 AI 多模态生图模型:Qwen Image Demo 实战
前端·人工智能
触底反弹3 小时前
一文彻底搞懂 JavaScript 栈和队列(建议收藏)
javascript·算法·面试
To_OC3 小时前
我一直以为 Ajax 是个黑盒,直到我写了这 50 行代码
前端·后端·全栈
用户059540174463 小时前
RAG 记忆层踩坑实录:用户偏好凭空消失,我排查了 4 小时,最后用 LangChain + Chroma 搭了套自动化回归测试
前端·css
Asize3 小时前
Prompt 驱动 NLP:从 ES6 模块化到文本推理实战
javascript·人工智能·机器学习