javascript函数中的var范围

时间:2012-10-15 21:04:44

标签: javascript closures scope

我有以下小代码片段,其中包含以下预期和实际输出。我的问题很简单。为什么按顺序打印呢?以及如何打印预期的输出?

GR,

预期结果:

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] ();
};

3 个答案:

答案 0 :(得分:5)

您推入数组的函数不会像创建函数时那样记录i的值,它们会在调用函数时记录i的值

第一个循环结束后,i的值为10,因此之后调用的任何函数都会记录值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);

};

答案 1 :(得分: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]();
};

现在,每个创建的函数都关闭在它自己的私有范围变量上,这可以解决问题。

答案 2 :(得分:0)

您应该将您的代码示例更新为i&lt; 3,以便您的结果和功能匹配。

你推入functions数组的函数是存储对变量i的引用,在执行top循环之后,它是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);
});