下面是它的工作原理. 每当声明一个新函数并将其赋值给变量时, 实际上是保存了函数定义和闭包. 闭包包含了创建函数时声明的所有变量, 就像一个背包一样 -- 函数定义附带一个小背包. 这个背包保存了创建函数时声明的所有变量.
所以我们上面的解释都是错误的, 让我们再试一次, 但这次是正确的:
- function createCounter() {
- let counter = 0
- const myFunction = function() {
- counter = counter + 1
- return counter
- }
- return myFunction
- }
- const increment = createCounter()
- const c1 = increment()
- const c2 = increment()
- const c3 = increment()
- console.log('example increment', c1, c2, c3)
第 1-8 行, 我们在全局执行上下文中创建了一个新变量 createCounter, 它包含了一个函数定义, 与上面相同.
第 9 行, 我们在全局执行上下文中声明一个名为 increment 的新变量, 与上面相同.
第 9 行, 我们调用 createCounter 函数并将其返回值赋给 increment 变量, 与上面相同.
第 1-8 行, 调用函数, 创建新的本地执行上下文, 与上面相同.
第 2 行, 在本地执行上下文中声明一个名为 counter 的新变量, 并赋值为 0, 与上面相同.
第 3-6 行, 在本地执行上下文中声明名为 myFunction 的新变量. 变量的内容是另一个函数的定义, 即第 4 行和第 5 行所定义的内容. 我们还创建了一个闭包并将其作为函数定义的一部分, 闭包含包含函数作用域内的变量, 在本例中为变量 counter(值为 0).
第 7 行, 返回 myFunction 变量的内容, 删除本地执行上下文. myFunction 和 counter 不再存在, 控制权返回到调用上下文. 所以我们返回函数定义及其闭包, 闭包中包含创建函数时声明的变量.
第 9 行, 在调用上下文 (全局执行上下文) 中, createCounter 返回的值被赋给 increment. 变量 increment 现在包含一个函数定义(和闭包), 其中函数定义由 createCounter 返回. 它不再被标记为 myFunction, 但定义是一样的. 在全局上下文中, 它被称为 increment.
第 10 行, 声明一个新变量(c1).
第 10 行, 查找变量 increment, 它是一个函数, 调用它, 它包含之前返回的函数定义, 也就是第 4-5 行所定义的内容(还有一个带变量的闭包).
创建新的执行上下文, 没有参数, 开始执行这个函数.
第 4 行, counter = counter + 1. 我们需要查找变量 counter. 在查看本地或全局执行上下文之前, 先让我们来看看闭包. 请注意, 闭包包含一个名为 counter 的变量, 其值为 0. 在第 4 行的表达式之后, 它的值被设置为 1, 然后再次保存在闭包中. 闭包现在包含了值为 1 的变量 counter.
第 5 行, 我们返回 counter 的值或数值 1, 销毁本地执行上下文.
返回第 10 行, 返回值 (1) 被分配给 c1.
第 11 行, 我们重复步骤 10-14. 这次, 我们可以看到变量 counter 的值为 1, 这个值是在第 4 行代码中设置的. 它的值加 1, 并在 increment 函数的闭包中存为 2.c2 被赋值为 2.
第 12 行, 我们重复步骤 10-14,c3 被设为 3.
第 13 行, 我们记录变量 c1,c2 和 c3 的内容.
所以现在我们了解闭包的工作原理. 当声明一个函数时, 它包含一个函数定义和一个闭包. 闭包是函数创建时声明的变量的集合.
你可能会问, 任何函数是否都有闭包, 包括在全局范围内创建的函数? 答案是肯定的. 在全局范围中创建的函数也会创建一个闭包. 但由于这些函数是在全局范围内创建的, 因此它们可以访问全局范围内的所有变量, 就无所谓闭包不闭包了.
当一个函数返回另一个函数时, 才会真正涉及闭包. 返回的函数可以访问仅存在于其闭包中的变量.
结 论
我通过背包类比的方式记住了闭包. 当创建和传递一个函数或将其从另一个函数返回时, 这个函数就带有一个背包, 背包中包含了所有在创建函数时声明的变量.
来源: http://www.bubuko.com/infodetail-2754676.html