我编写了这段代码来自学JavaScript关闭:
function1 = function(){
var variable = "foo"
var function2 = function(argument){
console.log(variable + argument);
}
return function2
}
function3 = function1();
function3("bar");
这打印" foobar"正如所料。但变量在哪里生活?
它是否成为function3的属性,还是存储在function3中的其他位置? JavaScript是否遍历某种闭包链,类似于它如何遍历原型链?它存储在其他地方的内存中吗?
我试图更深入地理解这一点。
答案 0 :(得分:32)
<强> TL; DR:强>
变量在哪里?
在环境中定义了它。
它是否成为function3的属性,还是存储在function3中的其他位置?
没有
JavaScript是否遍历某种闭包链,类似于它如何遍历原型链?
是
它是否存储在其他地方的内存中?
是
tl;博士2:
函数保持对它们所创建的环境的引用。当调用函数时,它创建一个新的环境,其父级是函数保留引用的环境。
更长的解释:
每当执行一个函数时,都会创建一个新的lexical environment。环境有两个“字段”:一个environment record,其中所有变量都被跟踪,一个外部词汇环境,顾名思义,引用“父词法环境”。
因此,当我们评估您的代码示例时,内存的初始状态(在执行任何操作之前)可能看起来像这样(简化):
+-(Global) lexical environment-+ +-Environment Record-+
+-------------+----------------+ +---------+----------+
| Environment | *--------+---> |function1|undefined |
| Record | | +---------+----------+
+-------------+----------------+ |function3|undefined |
| Outer | | +---------+----------+
| lexical | (empty) |
| environment | |
+-------------+----------------+
全球环境没有任何外部环境,因为它位于顶部。 function1
和function3
是两个尚未初始化的绑定(尚未评估作业)。
创建函数(评估function1 = function() { ... }
)后,内存如下所示:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+-------------+----------------+ |function3|undefined | | name |function1|
| Outer | | +---------+----------+ +---------------+---------+
| lexical | (empty) |
| environment | |
+-------------+----------------+
现在function1
有一个值,一个函数对象。函数对象具有多个内部(例如[[Environment]]
)和外部(例如name
)属性。顾名思义,无法从用户代码访问内部属性。 [[Environment]]
属性非常重要。注意它是如何引用函数创建的词法环境的!
下一步是执行function3 = function1()
,即调用function2
。正如我在一开始所说,每当执行一个函数时,就会创建一个新的词法环境。让我们在进入函数后查看内存:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| +---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+> +-------------+----------------+ |function3|undefined | | name |function1|
| | Outer | | +---------+----------+ +---------------+---------+
| | lexical | (empty) |
| | environment | |
| +-------------+----------------+
|
|
|
| +-----lexical environment------+ +-Environment Record-+
| +-------------+----------------+ +---------+----------+
| | Environment | *--------+--->|variable |undefined |
| | Record | | +---------+----------+
| +-------------+----------------+ |function2|undefined |
| | Outer | | +---------+----------+
| | lexical | * |
| | environment | | |
| +-------------+--------+-------+
| |
+-------------------------+
这看起来非常类似于全球环境的结构!我们有一个词法环境,其环境记录包含两个未初始化的绑定。但现在最大的不同是“外部词汇环境”指向全球词汇环境。怎么可能?
在调用function1
并创建新的词汇环境时,我们将新环境“外部词汇环境”字段的值设置为function1
的{{1}}字段的值。这是范围链的创建。
现在,在执行[[Environment]]
之后,内存具有以下结构:
function1
与 +------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+> +-------------+----------------+ |function3| | | | name |function1|
| | Outer | | +---------+---+------+ +---------------+---------+
| | lexical | (empty) | |
| | environment | | |
| +-------------+----------------+ +-------------------------+
| |
| +----------------------------------------------------------------+--------+
| v | |
| +-----lexical environment------+ +-Environment Record-+ v |
| +-------------+----------------+ +---------+----------+ |
| | Environment | *--------+--->|variable | 'foo' | +-----Function Object-+---+
| | Record | | +---------+----------+ +---------------+-----+---+
| +-------------+----------------+ |function2| *-----+---->|[[Environment]]| * |
| | Outer | | +---------+----------+ +---------------+---------+
| | lexical | * | | name |function2|
| | environment | | | +---------------+---------+
| +-------------+--------+-------+
| |
+-------------------------+
类似,function1
引用了通过调用function2
创建的环境。此外,function2
是指我们创建的函数,因为我们从function3
返回。
最后一步:致电function1
:
function3('bar')
此处类似,创建了一个新环境,其“外部词汇环境”字段指向调用 +------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+> +-------------+----------------+ |function3| | | | name |function1|
| | Outer | | +---------+---+------+ +---------------+---------+
| | lexical | (empty) | |
| | environment | | |
| +-------------+----------------+ +-------------------------+
| |
| +----------------------------------------------------------------+--------+
| v | |
| +-----lexical environment------+ +-Environment Record-+ v |
| +-------------+----------------+ +---------+----------+ |
| | Environment | *--------+--->|variable | 'foo' | +-----Function Object-+---+
| | Record | | +---------+----------+ +---------------+-----+---+
|+>+-------------+----------------+ |function2| *-----+---->|[[Environment]]| * |
|| | Outer | | +---------+----------+ +---------------+---------+
|| | lexical | * | | name |function2|
|| | environment | | | +---------------+---------+
|| +-------------+--------+-------+
++------------------------+
|
| +-----lexical environment------+ +-Environment Record-+
| +-------------+----------------+ +---------+----------+
| | Environment | *--------+--->|argument | 'bar' |
| | Record | | +---------+----------+
| +-------------+----------------+
| | Outer | |
| | lexical | * |
| | environment | | |
| +-------------+--------+-------+
+------------------------+
时创建的环境。
现在,查找function1
的值非常简单,因为它存在于环境自己的记录中。但是当查找argument
时,会发生以下情况:由于它在环境自己的记录中不存在,因此它会查看其“外部词汇环境”的记录。它可以做到这一点,因为它有一个参考。
答案 1 :(得分:4)
每当JavaScript执行function3函数时,都会创建一个'scope'对象来保存由您命名的局部变量作为变量(“foo”)。请注意,您的JavaScript代码无法直接访问此范围对象。因此,值“foo”可用于内部函数,但外部函数已返回。
JavaScript是否遍历某种闭包链,类似于它如何遍历原型链?
是。 “范围对象形成一个称为范围链的链,类似于JavaScript对象系统使用的原型链。
闭包是函数和创建它的范围对象的组合。闭包可以让你保存状态 - 因此,它们通常可以用来代替对象“
在这里阅读更多内容:
答案 2 :(得分:2)
变量存在于声明它们的范围内,可以是全局变量,也可以是函数。
此处的关键字为范围。
MSDN网站中的explained brilliantly:
在函数定义中声明的变量是本地的。它 每次执行函数时都会创建并销毁它 函数外的任何代码都无法访问。 JavaScript确实如此 不支持块范围(其中一组大括号{...}定义了一个 新范围),除了块范围变量的特殊情况。
修改强>:
实际上比这更复杂一点,请参阅toddmotto关于JS范围的帖子。
答案 3 :(得分:1)
关闭在MDN网站上得到了很好的解释。但我建议通过this awesome explanation in a Stack Overflow answer, a precise answer to your question。
这不是财产。只是在记忆中记住了环境。环境意味着功能以及范围和范围成员。我希望它能开辟道路。
答案 4 :(得分:1)
有关闭包如何工作的解释,请参阅此答案。
How do JavaScript closures work?
在虚拟机级别,每个函数都有自己的lexical environment,用于跟踪此信息。虚拟机查找在闭包中访问哪些变量并将它们存储在堆上,只要任何闭包可能需要它们,它们就会存在。
有关更深入的信息,请参阅以下两个重要内容: