JS闭包和执行上下文的创建

JS closures and creation of execution context

本文关键字:创建 上下文 执行 闭包 JS      更新时间:2023-09-26

你能在下面澄清我的误解吗?

如果 JS 中函数的执行上下文是在调用/调用函数时创建的,而不是在声明函数时创建的,那么在下面的代码中,内部是闭包吗?如果是这样,为什么?inner 尚未被调用,并且由于执行上下文是在调用时创建的,因此 inner 无法存储对 i 的引用。

function foo(i) {
   function inner() {
       return i;
   }
   return inner;
}
var f = foo(3);

内部何时引用 foo 的执行上下文?何时调用或何时定义?在上面的代码中,我还没有调用内部。

另外,如果您解释JS在看到函数定义时的作用(与函数调用/调用相反(,我将不胜感激

谢谢

请参阅有关其工作原理的评论。

function foo(i) {
   function inner() { // closure or a new scope is created
       return i;  // but parent scope's variable can be used in child scope
   }
   return inner; // returning reference to function
}
var f = foo(3); // f contains the function reference
f(); // 3, since i is in the parent scope, it is retained.

由于 i 是一个形式参数,因此可以在整个函数中访问它,因为它位于最顶层的范围,没有任何闭包问题。

JavaScript 中的范围是以函数为中心的。因此,每当执行函数时,它都会创建一个闭包或作用域。

在我看来,在相互矛盾的答案中可能存在术语问题。 有一种设计概念称为闭包,可以说它是允许闭包的代码设计。 这显然会在守则的声明中发生。 这是一个设计概念,但由于尚未执行任何代码,因此没有包含变量值、对函数的引用等的实际对象......

还有一个实际的函数对象,它

持续到执行其父函数的时间之后,这使得闭包实际工作。 该"闭包对象"是在执行时创建的,实际上,每次执行函数时都会创建一个新对象。

在您的案例中,将在执行foo()时创建实际的闭包对象。 而且,事实上,每次执行它(并将返回值保存到变量中(时,都会创建一个新的闭包。 代码声明为闭包创造了机会,但在执行函数 foo 之前,不会创建实际的闭包本身。

以下是此代码中发生的情况:

function foo(i) {
   function inner() {
       return i;
   }
   return inner;
}
var f = foo(3);
var result = f();

首先声明 foo() 函数。 声明它返回对 inner() 函数的引用。 这为关闭设置了机会,但尚未创建关闭。

然后,当您执行 f = foo(3) 时,将执行函数foo。 这将创建一个函数作用域对象(在解释器中(。 此函数 scope 对象中i的参数在此特定执行foo中具有 3 值,因此在此 scope 对象中也是如此。 当foo执行时,它会返回对inner的引用,然后将其分配给变量 f 。 因此,变量f包含对inner()的引用。

通常,当一个函数完成执行时,它的函数作用域对象会被垃圾回收(例如释放(,但在这种情况下,inner仍然具有对该作用域对象的引用,并且f包含对该inner调用的引用。 因此,该范围对象不符合垃圾回收的条件,并保持活动状态。 这称为闭包。 因为存在的东西对函数作用域内的东西有引用,所以不能像往常一样对其进行垃圾回收。

定义函数的行为为闭包创造了机会,但实际的闭包是在每次运行foo()时在执行时创建的。

事实上,如果你执行了f = foo(3),你创建了一个闭包,如果你执行g = foo(4),你又创建了一个闭包。 每次执行foo并将返回值保存在变量中时,都会创建一个闭包。 请记住,这种类型的闭包实际上只是一个函数范围对象,无法立即进行垃圾回收,因为某些代码保留了对该函数作用域中对象的引用,超出了函数本身的生存期。

就我个人而言,当我真的只是从垃圾收集器的角度考虑它们时,闭包更容易理解。 它们只是函数范围对象(所有函数都有(,还不能被垃圾回收,因为某些代码保留了对其中某些内容的引用。 而且,创建此类闭包的最常见方法是当外部代码被赋予对父函数内部本地函数的引用时。

当 JS 创建一个函数时,它会将其 scope 属性设置为等于当前环境:http://ecma-international.org/ecma-262/5.1/#sec-13.2

当JS执行一个函数时,它会创建一个新环境,并将其"外部"(=parent(指针设置为函数的作用域:http://ecma-international.org/ecma-262/5.1/#sec-10.4.3

所以你的问题的答案是:闭包是在定义函数时创建的,而不是在调用时创建的。为了澄清,每次程序到达"函数"行时都会创建函数对象。如果调用外部函数两次,将有两个不同的函数对象(具有两个不同的 [[作用域]](。

闭包是一对 a( 函数的代码和 b( 调用帧链。调用帧本质上是包含局部变量和参数的结构。

因此,当您返回该inner函数时,您将返回如下内容:

struct Function {
  var functionBody;     // compiled function body
  var outerScopeChain = { i:3,
                          nextScope: null };
};

JS中的每个函数对象实际上都是一个闭包,outerScopeChain全局函数为空。