函数访问变量环境,闭包

A function's access to variable environment, closures

我知道函数可以访问它们在其中创建的执行上下文的变量环境 (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)看到,但是可以手动访问吗?

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

let t = 1;

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

例如,您可以通过提供 return 值或对象本身的函数来获得访问权限。这里 getCounter 将 return 计数器的值。

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())

secureIncrement 返回的 incrementer 函数对象在第 4 步有一个名为 [[Environment]] set on it when it is created that points to the environment record (ER) it's defined in. In your example, the function you return has its [[Environment]] slot set to secureIncrement's ER which holds the binding for counter. The ER for secureIncrement is obtained from its execution context's lexical environment component (and not its variable environment component), which can be seen in InstantiateOrdinaryFunctionExpression step 2. This then calls OrdinaryFunctionCreate 的内部插槽,它设置返回的 incrementer[[Environment]] 插槽] 在第 14 步对那个 ER 起作用。

当调用 incrementer() 时,new function ER is created to store any bindings that might be created within its body (which in your example there are none). The new function ER also has its [[OuterEnv]] field set to the [[Environment]] of the incrementer function we just called, which can be seen in NewFunctionEnvironment step 8. This means that when you refer to counter within the increment function, the counter binding is first searched for in the new ER that was created when you called increment(), and if it can't find the binding in there, it looks for it in the [[OuterEnv]] of the new ER. In your example, as the counter binding doesn't exist directly in the increment function scope (and hence its ER), the ER referenced by [[OuterEnv]] is also searched. Above we saw that this slot is set from the [[Environment]] slot, which we also saw references the scope / ER of the secureIncrement function. As a result, when the [[OuterEnv]] is checked for a counter binding, a binding can successfully be found. Resolving the counter identifier is done as part of the steps in GetIdentifierReference 通过 [[OuterEnv]] 引用在作用域链上游递归搜索环境记录。

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