在JavaScript中,顶级函数是否会产生闭包?

lrl1mhuk  于 2022-10-30  发布在  Java
关注(0)|答案(3)|浏览(147)

我正在学习关闭和有基本的怀疑。

function makeCounter() {
  let count = 0;

  return function() {
    return count++;
  };
}

let counter = makeCounter();

这里我们返回函数时,它有一个带外部作用域的闭包,所以它有一个带count的闭包。

var x = 1;

function func() {
  console.log(x); //  does func has closure with x ??

}

func();

所以我的问题是,只有嵌套函数才能进行闭包??或者顶层函数也可以用x进行闭包??

sqxo8psd

sqxo8psd1#

JavaScript中的闭包是使用函数声明的[[EnvironmentRecord]]内部字段的[[OuterEnv]]内部字段实现的。然后,[[OuterEnv]]值被复制到每次调用函数时创建的执行上下文中。然后,该值被用于创建作用域链,从而启用闭包行为。
在全局上下文中声明的函数在这一点上没有什么不同,它们维护着到其周围环境(全局环境记录)的链接。
在全局上下文中,使用var和函数声明声明的变量的行为与在其他函数中声明的变量有细微的不同,因为它们最终作为全局对象上的属性,但这是实现细节。

lyfkaqu1

lyfkaqu12#

为了方便起见,我们假设每个闭包都创建了一个定义变量的“作用域”--闭包/作用域中的任何东西都可以访问变量,作用域之外的任何东西都不能。
这并不是对闭包如何工作的详尽描述,而是对与此问题相关的基本实用概念的描述。还有很多关于调用栈如何创建、词法作用域、NodeJS中的“全局”作用域、iframe、沙箱、varconst/let、块与函数作用域,和其他概念,我将留给读者自己去发现。

浏览器/窗口/全局范围

在浏览器中,您拥有顶级范围(基本上是window,通常称为“全局”作用域)。这种作用相当于一个闭包,因为所有“未包含”的变量都被定义为窗口作用域的一部分,并且可用于该页面上运行的大多数其他代码。例如,如果您在浏览器中包含以下脚本,则x是全局/窗口作用域的一部分,而yz则包含在它们自己的嵌套作用域中。
第一个

JavaScript模块

当你编写的JavaScript被其他模块导入或需要时,每个模块都会自动 Package 在自己的闭包中。例如,如果你要将上面的basic-script.js导入/需要到另一个JavaScript文件中,上面的代码会自动被如下所示括起来:

(function() {
  var x = 10;
  // ... other code removed for brevity
})();

还有一些其他的魔法可以用来公开导出等等,但这超出了这个答案的范围(双关语)。如果JS模块没有 Package 在它们自己的闭包中,那么您将遇到一堆命名冲突和引用问题,以及一场安全噩梦。
因此,回答你的问题,是的--你的第二个例子与“x”共享一个闭包--闭包是根据你的环境隐式创建的。

htzpubme

htzpubme3#

闭包的一个实际用途是创建一个保护范围,保护变量和函数不受外部干扰。顶层范围是“global”,使用window变量(一个对象)访问。任何东西,不管它在哪个范围内声明,都可以访问它。
在全局作用域中声明的函数创建了它自己的作用域,全局作用域或其他“子全局”作用域中的表达式都不能访问该作用域。在全局作用域中声明的函数或在其他函数中声明的函数创建了 * 它们自己的 * 作用域,甚至它的直接父作用域也不能访问该作用域。
在函数块内声明的任何变量-varletconst-都可以在该函数作用域和在该函数作用域内创建的任何子作用域中访问-即,该函数之外的任何东西都不能访问它,(除了创建函数可能提供的机制)。
问题中的第二个例子是x在全局作用域中声明,这意味着任何东西都可以访问x。如果x后来被更改,它将在所有作用域中被更改。x在代码中的任何地方都受到干扰。

var x = 1; <-- declared outside func()
function func() {
  // ...etc., etc., etc.

在第一个示例中:

function makeCounter() {
  let count = 0;

  return function() {
    return count++;
  };
}

... makeCounter()创建一个包含count和一个匿名函数的作用域,并且该作用域可以访问它们。该作用域中的子作用域也可以访问它们。但是,全局作用域或其他子全局作用域中的任何表达式都不能访问它们。
关于闭包给予从内部函数访问外部函数的作用域的描述-作用域访问“向上”行进,访问每个父作用域,一直到全局作用域,而不管它在“作用域链”中“向下”走多远。例如:

const can_i_reach_global = 'yes';
topLevel();
function topLevel () {
  console.log('topLevel func, can_i_reach_global:', can_i_reach_global);
  oneDeep();
  function oneDeep () {
    console.log('oneDeep func, can_i_reach_global:', can_i_reach_global);
    twoDeep();
    function twoDeep () {
      console.log('twoDeep func, can_i_reach_global:', can_i_reach_global)
    }
  }
}

因此,就实际的定义而言,简单地陈述函数闭包"...gives you access to an outer function's scope from an inner function..."并不能说明这个编程构造的有用性。

相关问题