javascript var declaration within loop
1 2 3 4 5 6 7 8 9 10 11 12 | /*Test scope problem*/ for(var i=1; i<3; i++){ //declare variables var no = i; //verify no alert('setting '+no); //timeout to recheck setTimeout(function(){ alert('test '+no); }, 500); } |
它按预期警告"设置1"和"设置2",但在超时后,它输出"测试2"两次-出于某种原因,变量"no"在第一个循环后不会重置…
我只找到了一个"丑陋"的解决方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 | /*Test scope problem*/ var func=function(no){ //verify no alert('setting '+no); //timeout to recheck setTimeout(function(){ alert('test '+no); }, 500); } for(var i=1; i<3; i++){ func(i); } |
关于如何更直接地解决这个问题有什么想法吗?还是只有这样?
Javascript没有块范围,变量声明被提升。这些事实加在一起意味着您的代码相当于:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | var no; /*Test scope problem*/ for(var i=1; i<3; i++){ //declare variables no = i; //verify no alert('setting '+no); //timeout to recheck setTimeout(function(){ alert('test '+no); }, 500); } |
当超时函数执行时,循环将很长时间结束,并且
解决这个问题的方法是将EDOCX1的当前值(1)传递到一个函数中,该函数为每个对EDOCX1的调用(9)创建一个新的回调。每次创建一个新函数意味着每个setTimeout回调都绑定到一个不同的执行上下文及其自己的变量集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | var no; /*Test scope problem*/ for(var i=1; i<3; i++){ //declare variables no = i; //verify no alert('setting '+no); //timeout to recheck setTimeout( (function(num) { return function() { alert('test '+num); }; })(no), 500); } |
这基本上与修复相同,但使用不同的语法来实现范围调整。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /*Test scope problem*/ for (var i = 1; i < 3; i++) { //declare variables var no = i; //verify no alert('setting ' + no); //timeout to recheck (function() { var n = no; setTimeout(function() { alert('test ' + n); }, 500); })(); } |
JavaScript没有词汇范围(for循环不创建新范围),您的解决方案是标准的解决方案。写这个的另一种方法可能是:
1 2 3 4 5 6 7 8 9 | [1, 2].forEach(function(no){ //verify no alert('setting '+no); //timeout to recheck setTimeout(function(){ alert('test '+no); }, 500); }) |
foreach()是在ecmascript 5中引入的,现在出现在现代浏览器中,但不是IE中。不过,您可以使用我的库来模拟它。
我喜欢我能从这个答案中得到很多里程数。
如果你需要帮助应用那个答案,请告诉我。
编辑当然。让我们看看您的原始代码。
1 2 3 4 | //timeout to recheck setTimeout(function(){ alert('test '+no); }, 500); |
看到那个匿名函数了吗?你进入埃多克斯的那个?直到计时器说是这样才调用它——在500毫秒时,在循环退出之后就可以调用了。
你所希望的是EDOCX1[1]被"就地"评估,但不是——它是在调用函数时评估的。到那时,
为了解决这个问题,我们需要一个在循环迭代期间执行的函数,它本身将返回一个
1 2 3 4 5 6 7 8 9 | setTimeout(function( value ) { // 'value' is closed by the function below return function() { alert('test ' + value ); } }( no ) // Here's the magic , 500 ); |
因为我们创建了匿名函数并立即调用它,所以已经创建了一个新的范围,可以在其中关闭变量。这个范围是在
因此,当setTimeout()激发时,它正在执行从闭包函数返回的函数。
我希望能解释清楚。