【问题标题】:A function's access to variable environment, closures函数对变量环境的访问,闭包
【发布时间】:2022-01-24 02:43:51
【问题描述】:

我知道函数可以访问它们在其中创建的执行上下文的变量环境 (VE),但是它们在哪里携带对它的引用,是否可以访问它?

编辑

例子:

function secureIncrement() {
    let counter = 0;

    return function () {
        counter++;
        console.log(`counter = ${counter}`);
    };
}

const incrementer = secureIncrement();
incrementer(); // 1
incrementer(); // 2
incrementer(); // 3

我知道incrementer 可以访问counter,但它在什么属性中保留对它的引用?

有一个[[Scopes]] 属性可以用console.dir(incrementer) 看到,但是可以手动访问吗?

【问题讨论】:

  • 你能详细说明你想做什么吗?
  • 这叫做闭包

标签: javascript closures


【解决方案1】:

secureIncrement 返回的incrementer 函数对象在创建时设置了一个名为[[Environment]] 的内部槽,它指向定义它的environment record (ER)。在您的示例中,您的函数return 将其 [[Environment]] 插槽设置为 secureIncrement 的 ER,该 ER 包含 counter 的绑定。 secureIncrement 的 ER 是从其 execution context 的词法环境组件(而不是其变量环境组件)中获得的,这可以在 InstantiateOrdinaryFunctionExpression 步骤 2 中看到。然后在步骤 4 调用 OrdinaryFunctionCreate,它设置在步骤 14 中返回的 incrementer 函数的 [[Environment]] 槽到该 ER。

当调用incrementer() 时,会创建一个new function ER 来存储可能在其主体中创建的任何绑定(在您的示例中没有)。新函数 ER 还将其 [[OuterEnv]] 字段设置为我们刚刚调用的 incrementer 函数的 [[Environment]],这可以在 NewFunctionEnvironment 步骤 8 中看到。这意味着当您在increment 函数,首先在调用increment() 时创建的新ER 中搜索counter 绑定,如果在其中找不到绑定,则在[[OuterEnv]] 中查找新的急诊室。在您的示例中,由于counter 绑定不直接存在于increment 函数范围(以及它的ER)中,因此[[OuterEnv]] 引用的ER 也会被搜索。上面我们看到这个槽是从[[Environment]] 槽设置的,我们还看到引用了secureIncrement 函数的作用域/ER。因此,当检查[[OuterEnv]]counter 绑定时,可以成功找到绑定。解析counter 标识符是GetIdentifierReference 中步骤的一部分,该步骤通过[[OuterEnv]] 引用沿作用域链向上递归搜索环境记录。

没有公开这些范围的 API,因此您无法在 JavaScript 代码中手动访问它们。

【讨论】:

    【解决方案2】:

    这里控制台将打印 50,因为 t 的范围在函数之外。传递给 console.log 的值是一个引用,这意味着当我更新原始值时,将打印的值也会发生变化。

    let t = 1;
    
    setTimeout(() => console.log(t), 100);
    t = 50

    例如,您可以通过提供返回值或对象本身的函数来访问。这里getCounter会返回计数器的值。

    function secureIncrement() {
      let counter = 0;
    
      return {
        inc: function() {
          counter++;
          console.log(`counter = ${counter}`);
        },
        getCounter: () => counter
      };
    }
    
    const {
      inc,
      getCounter
    } = secureIncrement();
    inc(); // 1
    console.log('myCounter: ', getCounter())
    inc(); // 2
    console.log('myCounter: ', getCounter())
    inc(); // 3
    console.log('myCounter: ', getCounter())

    【讨论】:

    • 这显示了一个闭包示例,但没有回答问题。您的示例的问题是,“匿名函数如何存储对t 的引用?内存中的引用保存在哪里?函数原型上是否有引用?如果是,我该如何访问它?” .
    猜你喜欢
    • 1970-01-01
    • 2012-10-28
    • 2020-12-02
    • 2013-04-24
    • 2013-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-19
    相关资源
    最近更新 更多