JavaScript关闭行为异常

时间:2019-05-24 20:31:48

标签: javascript closures

执行此代码时,我希望在控制台中看到10次写入100次(基于使用其他语言的经验):

const arr = [];
for (let i = 0; i < 10; i++)
    arr.push(() => i * i);

arr.forEach(f => console.log(f()));

但是我得到每个数字的平方(从0到9)。这似乎与某些其他语言有所不同。

例如,使用 C#,您将获得100十次:

    var list = new List<Func<int>>();

    for(var i = 0; i < 10; i++)
        list.Add(() => i * i);

    list.ForEach(f => Console.WriteLine(f()));

Python 会打印81次十次:

    arr = []
    for i in range(10):
        arr.append(lambda: i * i)

    for f in arr:
        print(f())

Java 不允许这样做,因为捕获的变量无法突变。

有关其他语言的示例,请参见this

如下文Mark所述,当我们在JavaScript中使用 let (块作用域)时,i的值被捕获在每个匿名函数内的新作用域变量中。当您查看每个函数的实际定义时(使用: arr.forEach(f => console.dir(f)); ),您会看到一个范围(_loop)来自每次迭代都会捕获到我的价值:

  [[Scopes]]: Scopes[3]
  0: Closure (_loop) {i: 0}
  [[Scopes]]: Scopes[3]
  0: Closure (_loop) {i: 1}
   and so on...

如果我们使用 var 重新运行此示例,毫不奇怪的是,缺少_loop块级作用域,并且我以相同的值(10)保存在模块/文件级作用域中每次迭代:

  [[Scopes]]: Scopes[2]
  0: Closure (./src/index.js) {i: 10}

1 个答案:

答案 0 :(得分:3)

这是使用let在javascript中声明变量的本质-它的作用域是for循环块(在此暗示),这意味着每次迭代都会获得一个新的作用域名称,该名称在每个关闭。闭包并非全都使用相同的名称,就像在其他语言中使用闭包时一样,或者在使用var声明变量时使用JS时也是如此。

要获得预期的行为,请使用var

const arr = [];
for (var i = 0; i < 10; i++)
    arr.push(() => i * i);

arr.forEach(f => console.log(f()));

上面的代码是许多困惑的程序员的祸根,这是可以选择使用let的原因之一。