我有两个代码块,我认为应该产生相同的结果:
1
for(var i=0;i<10;i+=1){
var j=i;
setTimeout(function(){
console.log(j);
},100);
}
2
for(var i=0;i<10;i+=1){
(function(j){
setTimeout(function(){
console.log(j);
},100);
})(i);
}
然而,正如大多数人所预料的那样,第一个记录9次,第二次记录正确,从0到9。
第二个是使用闭包来保留i的值。我认为第一个也应该保留价值,因为:
var j
在每次迭代中创建一个新变量j。i
值已在迭代中分配给此新j
。j
然后在同一次迭代中绑定到setTimeout
的函数。但事实证明,j
绑定到所有迭代的最后i
值。
那么,使用函数参数和var
创建变量之间的区别是什么?
请指出任何错误!提前谢谢!
谢谢大家!我不知道javascript只有功能和全局范围!归咎于教会我这样做的其他语言:P
答案 0 :(得分:5)
第二个是使用闭包来保留i的值。
事实上,你所看到的两个结果都是闭包工作的结果。您传递给setTimeout
的两个函数都是闭包。
我认为第一个也应保留价值,因为:
var j在每次迭代中创建一个新的变量j。
不,它没有。您的第一个示例中只有一个 j
。在JavaScript(目前)中,变量仅具有函数或全局范围,从不阻止范围。 JavaScript实际上对你的第一个例子做了什么看起来更像是这样:
var i;
var j;
for(i=0;i<10;i+=1){
j=i;
setTimeout(function(){
console.log(j);
},100);
}
结果闭包有一个持久引用到一个j
变量,这就是为什么你一遍又一遍地得到相同的值。
你的第二个例子是一个反模式,因为它既难以阅读,又在每个循环中不必要地创建和丢弃函数。让我们通过添加一些变量来使其变得更加明显;这段代码与第二个例子完全相同,唯一的变化是我添加的中间变量:
for(var i=0;i<10;i+=1){
var f1 = function(j){
var f2 = function(){
console.log(j);
};
setTimeout(f2,100);
};
f1(i);
}
相反:
for(var i=0;i<10;i+=1){
setTimeout(makeHandler(i));
}
function makeHandler(j){
return function(){
console.log(j);
};
}
更容易阅读,避免在每个循环上重新创建makeHandler
函数。
更多探索(在我的博客上):
答案 1 :(得分:1)
它是setTimeout,它推迟执行直到浏览器可用,或者在这种情况下0.1秒后,并且当循环阻塞浏览器时,timeOuts直到循环完成后才会执行,到那时变量i
等于它在循环中设置的最后一个东西,因为循环在setTimeout中的代码执行之前已经完成。
通过将变量作为参数传递给另一个函数作为循环内部的参数,该变量对于该函数作用域是本地的,并保持其值与迭代无关。
for(var i=0;i<10;i+=1){
var j=i; // j is constantly updated and it's also hoisted
setTimeout(function(){ // and this runs last, when the browser is no longer
console.log(j); // locked, so j is the last value it was set to
},100);
}
和
for(var i=0;i<10;i+=1){
(function(j){ // creates a new scope with a local variable, the argument j
setTimeout(function(){
console.log(j);
},100);
})(i); // passing i to the functions scope
}
答案 2 :(得分:1)
在您的第一个代码示例中,j
未绑定循环的迭代中i
的值。这是因为您无法在循环中声明变量。循环在JavaScript中没有范围,只有函数具有范围。
第一个代码示例相当于:
var i, j; // all variable declarations are hoisted to the top
for (i = 0; i < 10; i += 1) {
j = i;
setTimeout(function() {
console.log(j);
}, 100);
}
因为在第二个代码中,样本j
在anonymous-immediate函数中声明,所以在该迭代中它被绑定到i
的值,因为值为的传递 / em>的。这称为关闭范围或只是闭包。
答案 3 :(得分:0)
var j在每次迭代中创建一个新的变量j。
您认为不幸的是Javascript的想法。在Javascript中,所有变量声明都移动到封闭函数的顶部,因此当您键入
时function(){
for(var i=1; i<10; i++){
Javascript实际上将其理解为
function(){
var i;
for(i=1; i<20; i++){
这个变量提升在带闭包的for循环中最容易混淆,但它也发生在其他类型的块中。例如,在具有常规词汇范围的语言中,以下代码将打印&#34; 1&#34;因为内心&#34; x&#34;是一个单独的变量,只存在于if语句的那个分支中:
var x = 1;
if(true){
var x = 2;
}
console.log(x);
然而,在Javascript中,内部变量声明被提升出来,所以它实际上好像你已经写了
var x;
x = 1;
if(true){
x = 2;
}
console.log(x);
答案 4 :(得分:0)
这是有效的,使用let
代替var
(let
在2013年提出问题的时候并没有回复:
for(let i=0;i<10;i+=1){
let j=i;
setTimeout(function(){
console.log(j);
},100);
}