0

我有以下带有以下预期和实际输出的小代码片段。我的问题很简单。为什么要按这个顺序打印?以及如何打印预期的输出?

格,

预期结果:

0
1
2
0
1
2

实际结果:

0
1
2
3
3
3

这是代码:

var functions = [];

for (var i=0; i<10; i++) {
  console.log (i);
  functions.push (function () {
    console.log (i);
  });
};

for (var j=0; j<functions.length; j++) {
  functions[j] ();
};
4

3 回答 3

5

推入数组的函数不会记录i函数创建时的值,它们会记录i调用函数时的值。

一旦第一个循环结束,值为iis 10,因此之后调用的任何函数都会记录 value 10

如果要保留i不同状态的值,可以使用闭包来复制该值:

for (var i=0; i<10; i++) {
  console.log (i);

  (function(){
    var copy = i;

    functions.push (function () {
      console.log (copy);
    });

  })();

};

局部变量copy将获取i并保留该值。您还可以将值作为参数传递给函数:

for (var i=0; i<10; i++) {
  console.log (i);

  (function(copy){

    functions.push (function () {
      console.log (copy);
    });

  })(i);

};
于 2012-10-15T21:10:25.547 回答
2

预期的结果应该是:

1
2
...
10
10
10
... 7 more times

这样做的原因是简单的。循环内部console.log(i)正确打印循环i每次迭代的值。当您创建一个函数并将其推送到functions数组中时,您所做的就是在同一个变量上关闭每个函数i。在你的循环结束时,i不再满足你的循环条件,所以i = 10是真的。结果,由于这些函数中的每一个都将执行console.log(i),并且它们每个都在相同的 上关闭i,现在的值为 10,您应该期望看到值 10 打印了 10 次。

为了防止这种情况,您需要创建一个返回函数的函数,而不是直接在循环中创建函数:

var functions = [], i, j;
function createEmitter(i) {
  return function () {
    console.log(i);
  };
}

for (i = 0; i < 10; i++) {
  console.log(i);
  functions.push(createEmitter(i));
};

for (j = 0; j < functions.length; j++) {
  functions[j]();
};

现在,这些创建的函数中的每一个都在其自己的私有范围变量上关闭,从而解决了问题。

于 2012-10-15T21:12:01.130 回答
0

您应该将代码示例更新为 i < 3,以便您的结果和函数匹配。

您推入functions数组的函数存储对变量 i 的引用,在执行顶部循环后,该引用为 10。因此,当它执行时,它将获取变量 i(即 10)并打印 10 次。

这是一个很好的方法来查看它的实际效果:

for (var i=0; i<10; i++) {
  console.log (i);
};

console.log(i) //=> 10

使用变量时,请记住变量可以更改,它不会冻结在当前值。你只是坚持对其他事物的引用。

为了解决这个问题,我会在代码上运行这种类型的小重构(因为其他答案已经创建了一个额外的范围,我想我会给你一些不同的东西)。与其存储 10 个函数,不如存储数字并使用单个函数执行它们。无论如何,这是一种更优雅的编写方式,并且占用的空间更少。我确信这个例子是从真正给你问题的任何代码中抽象出来的,但一般模式仍然适用。

numbers = [];
for (var i=0; i<10; i++) {
  console.log (i);
  numbers.push(i);
};

numbers.forEach(function(i){
   console.log(i);
});
于 2012-10-15T21:23:37.840 回答