tl;博士:
变量在哪里?
在它被定义的环境中。
它会成为 function3 的一个属性,还是存储在 function3 的其他地方?
不。
JavaScript 是否遍历某种闭包链,类似于它遍历原型链的方式?
是的。
它是否存储在其他地方的内存中?
是的。
tl;博士2:
函数保持对创建它们的环境的引用。当一个函数被调用时,它会创建一个新环境,其父环境是函数保持引用的环境。
更长的解释:
每当执行一个函数时,就会创建一个新的词法环境。环境有两个“字段”:一个是跟踪所有变量的环境记录和一个外部词法环境,顾名思义,它指的是“父词法环境”。
因此,当我们评估您的代码示例时,内存的初始状态(在执行任何操作之前)可能如下所示(简化):
+-(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
'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 | | | +---------------+---------+
| +-------------+--------+-------+
| |
+-------------------------+
与 like 类似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
,会发生以下情况:由于它不存在于环境自己的记录中,它会查看其“外部词法环境”的记录。它可以这样做,因为它有一个对它的引用。