javascript - JavaScript 闭包位于哪里?

标签 javascript closures

我编写这段代码是为了自学 JavaScript 闭包:

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

function3 = function1();
function3("bar");

这将按预期打印“foobar”。但变量存在于哪里?

它会成为 function3 的属性,还是存储在 function3 的其他地方? JavaScript 是否会遍历某种闭包链,类似于它遍历原型(prototype)链的方式?它是否存储在内存中的其他地方?

我正在尝试更深入地理解这一点。

最佳答案

tl;博士:

where does the variable live?

在定义它的环境中。

Does it become a property of function3, or stored somewhere else in function3?

没有。

Does JavaScript traverse some kind of closure chain, similarly to how it traverses the prototype chain?

是的。

Is it stored in memory somewhere else?

是的。

<小时/>

tl;博士2:

函数保留对其创建所在环境的引用。调用函数时,它会创建一个新环境,其父环境是函数保留引用的环境。

<小时/>

更长的解释:

每当执行一个函数时,都会生成一个新的 lexical environment被 build 。环境有两个“字段”: environment record其中所有变量都被跟踪,并且外部词法环境,顾名思义,指的是“父词法环境”。

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

+-(Global) lexical environment-+     +-Environment Record-+
+-------------+----------------+     +---------+----------+
| Environment |       *--------+---> |function1|undefined |
|   Record    |                |     +---------+----------+
+-------------+----------------+     |function3|undefined |
|    Outer    |                |     +---------+----------+
|   lexical   |    (empty)     |
| environment |                |
+-------------+----------------+

全局环境没有任何外部环境,因为它位于顶部。 function1function3 是两个尚未初始化的绑定(bind)(尚未评估赋值)。

创建函数后(计算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 |        |       |
|  +-------------+--------+-------+
|                         |
+-------------------------+

这看起来与全局环境的结构非常相似!我们有一个词法环境,它有一个带有两个未初始化绑定(bind)的环境记录。但现在最大的区别是“外部词汇环境”指向全局词汇环境。这怎么可能?

当调用function1并创建一个新的词法环境时,我们将新环境的“外部词法环境”字段的值设置为function1的值>[[环境]] 字段。这是创建作用域链的地方。

现在,执行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 的值非常简单,因为它存在于环境自己的记录中。但是当查找变量时,会发生以下情况:由于它不存在于环境自己的记录中,因此它会查看其“外部词法环境”的记录。它可以做到这一点,因为它有一个对它的引用。

关于javascript - JavaScript 闭包位于哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37491626/

相关文章:

javascript - __proto__ 与 constructor.prototype 有何不同?

javascript - Angularjs:理解递归指令

ios - 传递 "partial closure"的优雅方式?

Javascript 闭包和内存泄漏风险

javascript - 为什么 JavaScript 中这些基于闭包的私有(private)变量不起作用

javascript - JavaScript 循环中闭包的区别

javascript - 迭代数组中未定义的方法

javascript - 推迟 Vue block 的渲染,直到数据加载

javascript - 如何使用 javascript 或 nodejs 更新具有公共(public)头文件的链接

javascript - [[范围]] 和执行上下文