JavaScript关闭在哪里?

Wyl*_*udd 27 javascript closures

我写了这段代码来自学JavaScript关闭:

function1 = function(){
  var variable = "foo"
  var function2 = function(argument){
    console.log(variable + argument);
  }
  return function2
}

function3 = function1();
function3("bar");
Run Code Online (Sandbox Code Playgroud)

按预期打印"foobar".但变量在哪里生活?

它是否成为function3的属性,还是存储在function3中的其他位置?JavaScript是否遍历某种闭包链,类似于它如何遍历原型链?它存储在其他地方的内存中吗?

我想更深入地了解这一点.

Fel*_*ing 32

TL;博士:

变量在哪里生活?

在它所定义的环境中.

它是否成为function3的属性,还是存储在function3中的其他位置?

没有.

JavaScript是否遍历某种闭包链,类似于它如何遍历原型链?

是.

它存储在其他地方的内存中吗?

是.


tl;博士2:

函数保留对它们所创建的环境的引用.当调用函数时,它创建一个新的环境,其父级是函数保留引用的环境.


更长的解释:

每当执行一个函数时,都会创建一个新的词法环境.环境有两个"字段":一个环境记录,其中所有变量都被跟踪,外部词汇环境,如名称所示,指向"父词法环境".

因此,当我们评估您的代码示例时,内存的初始状态(在执行任何操作之前)可能看起来像这样(简化):

+-(Global) lexical environment-+     +-Environment Record-+
+-------------+----------------+     +---------+----------+
| Environment |       *--------+---> |function1|undefined |
|   Record    |                |     +---------+----------+
+-------------+----------------+     |function3|undefined |
|    Outer    |                |     +---------+----------+
|   lexical   |    (empty)     |
| environment |                |
+-------------+----------------+
Run Code Online (Sandbox Code Playgroud)

全球环境没有任何外部环境,因为它处于最顶层.function1并且function3是尚未初始化(分配尚未评估)两个绑定.

创建函数(评估function1 = function() { ... })后,内存如下所示:

            +------------------------------------------------------------------------+
            |                                                                        |
            v                                                                        |
+-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+
+-------------+----------------+    +---------+----------+     +---------------+-----+---+
| Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   |
|   Record    |                |    +---------+----------+     +---------------+---------+
+-------------+----------------+    |function3|undefined |     |     name      |function1|
|    Outer    |                |    +---------+----------+     +---------------+---------+
|   lexical   |    (empty)     |
| environment |                |
+-------------+----------------+
Run Code Online (Sandbox Code Playgroud)

现在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 |        |       |
|  +-------------+--------+-------+
|                         |
+-------------------------+
Run Code Online (Sandbox Code Playgroud)

这看起来非常类似于全球环境的结构!我们有一个词法环境,其环境记录包含两个未初始化的绑定.但现在最大的不同是"外部词汇环境"指向全球词汇环境.怎么可能?

在调用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 |        |       |                               +---------------+---------+
|  +-------------+--------+-------+
|                         |
+-------------------------+
Run Code Online (Sandbox Code Playgroud)

类似的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 |        |       |
 | +-------------+--------+-------+
 +------------------------+
Run Code Online (Sandbox Code Playgroud)

类似于此处,创建了一个新环境,其"外部词汇环境"字段指向function1被调用时创建的环境.

现在,查找值argument很简单,因为它存在于环境自己的记录中.但是当查找时variable,会发生以下情况:由于它不存在于环境自己的记录中,因此它会查看其"外部词汇环境"的记录.它可以做到这一点,因为它有一个参考.

  • 有没有用于绘制这些ASCII图的特殊工具? (3认同)