tl;dr:
变量住在哪里?
在它被定义的环境中。
它会成为 function3 的属性,还是存储在 function3 的其他地方?
No.
JavaScript 是否会遍历某种闭包链,类似于它遍历原型链的方式?
Yes.
它存储在内存中的其他地方吗?
Yes.
tl;dr 2:
函数保留对其创建所在环境的引用。调用函数时,它会创建一个新环境,其父环境是函数保留引用的环境。
更长的解释:
每当执行一个新函数时词汇环境 http://www.ecma-international.org/ecma-262/6.0/index.html#sec-lexical-environments被建造。环境有两个“场”:环境记录 http://www.ecma-international.org/ecma-262/6.0/index.html#sec-environment-records其中所有变量都被跟踪并且外部词汇环境顾名思义,它指的是“父词汇环境”。
因此,当我们评估您的代码示例时,内存的初始状态(在执行任何操作之前)可能如下所示(简化):
+-(Global) lexical environment-+ +-Environment Record-+
+-------------+----------------+ +---------+----------+
| Environment | *--------+---> |function1|undefined |
| Record | | +---------+----------+
+-------------+----------------+ |function3|undefined |
| Outer | | +---------+----------+
| lexical | (empty) |
| environment | |
+-------------+----------------+
全局环境没有任何外部环境,因为它位于顶部。function1
and function3
是两个尚未初始化的绑定(尚未评估赋值)。
创建函数后(评估function1 = function() { ... }
),内存看起来像这样:
+------------------------------------------------------------------------+
| |
v |
+-(Global) lexical environment-+ +-Environment Record-+ +-----Function Object-+---+
+-------------+----------------+ +---------+----------+ +---------------+-----+---+
| Environment | *--------+--->|function1| *-----+---->|[[Environment]]| * |
| Record | | +---------+----------+ +---------------+---------+
+-------------+----------------+ |function3|undefined | | name |function1|
| Outer | | +---------+----------+ +---------------+---------+
| lexical | (empty) |
| environment | |
+-------------+----------------+
Now 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
's [[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
很简单,因为它存在于环境自己的记录中。但抬头一看variable
,会发生以下情况:由于它不存在于环境自己的记录中,因此它会查看其“外部词汇环境”的记录。它可以做到这一点,因为它有一个对它的引用。