简单了解 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 函数返回一个闭包,它可以延迟执行一段代码,以便在指定的时间后输出消息。

参考资料

相关推荐
天天扭码1 分钟前
总所周知,JavaScript中有很多函数定义方式,如何“因地制宜”?(ˉ﹃ˉ)
前端·javascript·面试
一个专注写代码的程序媛6 分钟前
为什么vue的key值,不用index?
前端·javascript·vue.js
장숙혜17 分钟前
ElementUi的Dropdown下拉菜单的详细介绍及使用
前端·javascript·vue.js
火柴盒zhang20 分钟前
websheet之 编辑器
开发语言·前端·javascript·编辑器·spreadsheet·websheet
某公司摸鱼前端22 分钟前
uniapp 仿企微左边公司切换页
前端·uni-app·企业微信
WKK_25 分钟前
uniapp自定义封装tabbar
前端·javascript·小程序·uni-app
莫问alicia26 分钟前
react 常用钩子 hooks 总结
前端·javascript·react.js
Mintopia35 分钟前
图形学中的数学基础与 JavaScript 实践
前端·javascript·计算机图形学
Mintopia41 分钟前
Three.js 制作飘摇的草:从基础到进阶的全流程教学
前端·javascript·three.js
BillKu42 分钟前
Vue3父子组件数据双向同步实现方法
前端·javascript·vue.js