问题原因
首先让我们来看一段代码
function foo(){
for (var i = 0;i<3;i++){
console.log(i)
}
}
function foo1(){
for (var i = 0;i<3;i++){
setTimeout( function timer() { console.log( i ); }, i*1000 );
}
}
foo() //0 1 2
foo1() // 3 3 3
foo
和foo1
同样是for
循环。为什么一个输出0 1 2
,一个输出3 3 3
呢?而我们希望 foo1
也输出 0 1 2
该怎么解决呢?这个就与 js 的作用域相关了。当var
声明变量是不会有块级作用域的,上述代码相当于。
function foo(){
{
var i = 0
console.log(i)
}
{
var i = 1
console.log(i)
}
{
var i = 2
console.log(i)
}
{
var i = 3//终止循环了
}
}
function foo1(){
{
var i = 0
setTimeout( function timer() { console.log( i ); }, i*1000 );
}
{
var i = 1
setTimeout( function timer() { console.log( i ); }, i*1000 );
}
{
var i = 2
setTimeout( function timer() { console.log( i ); }, i*1000 );
}
{
var i = 3//终止循环了
}
}
foo() //0 1 2
foo1() // 3 3 3
如上述代码,foo会输出 0 1 2
,就很好理解。foo1()
遇到setTimeout,setTimeout内部的函数会放到循环执行完毕之后去执行它。而这时 i
的值 已经是 3了所以他们会输出3 3 3
。解决这个问题很好解决,因为我们知道是因为 var
没有产生块级作用域。
解决方法1
所以我们用let
就阔以解决它了。
function foo1(){
for (let i = 0;i<3;i++){
setTimeout( function timer() { console.log( i ); }, i*1000 );
}
}
foo1(); //0 1 2
那么假如我们不想使用let
,该如何解决呢?也有办法。
解决方法2
function foo1(){
for (var i=0; i<3; i++) {
(function(j) {
setTimeout(
function timer() {
console.log( j );
}, j*1000 );
})( i );
}
}
foo1(); //0 1 2
我们利用立即执行函数去接收i
, 函数内部作用域中,j
就是i
的值,不会i
的改变影响。