JavaScript 学院 #2:闭包

2025-05-25

JavaScript 学院 #2:闭包

欢迎来到这个新学院!在这里,我不会从头开始讲解 JavaScript,而是会讲解一些 JavaScript 概念,帮助你理解 JavaScript 引擎!

今天我就来教大家什么是Closure

简短定义

AClosure是一个可以访问其自身范围之外的变量的函数。

🤔...

好吧,我们来举个例子!

let b = 5

function toto(a) { return a + b }
toto(1) // 6
Enter fullscreen mode Exit fullscreen mode

当 JS 引擎将传递给函数时,它将检查每个变量是否在当前上下文中可用,这里只有变量a可用,因为它是参数。

但是当检查b变量时,它会检查该变量在函数上下文中是否可用!所以它会检查该变量在外部上下文中是否可用!

这才叫一个Closure

但是 JS 引擎需要采取一些技巧才能在函数中保留外部变量访问!

实际上,JS 引擎会将外部变量(b)存储在内存中heap!因此,它会保留对该变量的引用,当我们调用该函数时,该变量将可用!

事实上这outer variable是存储在一个对象名称中Lexical environment

什么是Lexical environment

词法环境只是“理论上”存在!它赋予函数调用所需的一切!它由两部分组成:

  • 存储局部变量的环境记录。

  • 引用此函数中使用的外部变量。

现在让我们看看 JS 引擎如何在函数中管理变量作用域

const toto = 5 // declared in the global scope

function hello () = {
   let a = 35
   let toto = 45
   console.log(a + toto)
}

hello() // 80
Enter fullscreen mode Exit fullscreen mode

为什么在上面的例子中,当我们调用console.log(a + toto)toto 时,它的值是45而不是5

当我们调用一个函数时,JS 引擎会检查变量是否存在于当前上下文(环境记录)中,如果是,则取该值,否则,它会检查变量是否存在于外部上下文中,直到到达Global Scope

另一个例子来理解这种行为!

const toto = 'toto global | ' // declared in the global scope
const b = 'b global | '

function hello () {
   const a = 'a hello | '
   const toto = 'toto hello | '
   const c = 'c hello | '

   return function hey () {
      const c = 'c hey | '
      console.log(a + toto + b + c)
   }
}

hello()() // a hello | toto hello | b global | c hey |
Enter fullscreen mode Exit fullscreen mode

你看懂其中的逻辑了吗?

目的Closure

它使用更多的资源,那么为什么要使用闭包?

此外,我们还可以创造副作用!

let toto = 55

function change() {
   toto = 69
}

change()
console.log(toto) // 69
Enter fullscreen mode Exit fullscreen mode

但是您可以封装数据并创建一些秘密和受保护的变量!

function hello() {
   let counter = 0
   return function () {
      counter++
      console.log(counter)
   }
}

const upCounter = hello()
upCounter() // 1
upCounter() // 2
upCounter() // 3
Enter fullscreen mode Exit fullscreen mode

您的变量是安全的,您只能通过函数返回来更改它hello

此外,每个实例hello()都有其自己的上下文!

const upCounter1 = hello()
const upCounter2 = hello()

upCounter1() // 1
upCounter1() // 2

upCounter2() // 1
Enter fullscreen mode Exit fullscreen mode

关于闭包的测验

const arrFuncs = []

for(var i = 0; i < 5; i++) {
  let toto = function () {
    return i
  }

  arrFuncs.push(toto)
}

console.log(i) // i is 5

arrFuncs.forEach(arrFunc => console.log(arrFunc())) // All logs
Enter fullscreen mode Exit fullscreen mode

为什么它记录了5而不是记录了0, 1, 2, 3, 4?!

让我们一步一步地理解这一点!

for(var i = 0; i < 5; i++)
Enter fullscreen mode Exit fullscreen mode

等于

var i
for(i = 0; i < 5; i++)
Enter fullscreen mode Exit fullscreen mode

由于我们使用了 var,因此变量i被提升到global scope!

所以当我们做

  let toto = function () {
    return i
  }
Enter fullscreen mode Exit fullscreen mode

我们正在使用 toto 函数作为Closure

我们知道,闭包用于reference外部变量(var i 是一个外部变量,因为它在全局范围内声明)

因此,当我们执行每个闭包时(在期间forEach),我们将从中获取变量值reference,此时的当前值是5

所以这就是我们console log 5五次的原因!


我希望你喜欢这篇文章!

🎁 如果您在Twitter上关注我并给我发送消息,您可以Underrated skills in javascript, make the difference免费获得我的新书😁 并节省 19 美元💵💵

或者在这里获取

🇫🇷🥖 对于法国开发者,你可以查看我的YoutubeChannel

🎁我的新闻通讯

☕️ 你可以支持我的作品🙏

🏃‍♂️ 你可以关注我 👇

🕊 推特:https://twitter.com/code__oz

👨‍💻 Github:https://github.com/Code-Oz

你也可以标记🔖这篇文章!

文章来源:https://dev.to/codeoz/javascript-academy-2-closures-2j78
PREV
launch.json 将 VS Code 转变为完整的 Web 开发 IDE
NEXT
JavaScript 学院 #1:原始值 vs 引用值