匿名函数中的Javascript范围问题

时间:2014-04-07 19:06:56

标签: javascript

我在Google上搜索与海报类似的问题时,发现了一个帖子(https://bbs.archlinux.org/viewtopic.php?id=58640)。

他试图找出下面代码的修复方法。

for(var i=0; i<3; i++) {
    req[i] = new XMLHttpRequest();
    req[i].onreadystatechange = function() {

        if(req[i].readyState == 4 && req[i].status == 200) {
            URL = url[i];
            success();
        } 

    }
    req[i].open("GET", url[i], true);
    req[i].send(null);    
}

解决方案是

for(var i=0; i<3; i++) {
    req[i] = new XMLHttpRequest();
    req[i].onreadystatechange = function(index) {
              return function() {
                    if(req[index].readyState == 4 && req[index].status == 200) {
                    URL = url[index];
                    success();
                   }
        };
    }(i);
    req[i].open("GET", url[i], true);
    req[i].send(null);    
}

这是因为req[i]的范围问题。

我在i内部使用类似函数测试了onreadystatechange的值,它打印了2,2,2,而不是0,1,2。

显然有i值发生了什么事,但我不确定发生了什么。

1 个答案:

答案 0 :(得分:0)

此问题可能会简化为

for(var i=0; i<3; i++) {
    setTimeout(function() {
        console.log(i);
    }, 0)
}

它输出333而不是012

问题是函数中的i与循环中使用的i相同,并且由于函数是在循环的所有迭代之后调用的,因此始终为3

您可以使用ES6 let修复它:

for(var i=0; i<3; i++) {
    let j=i;
    setTimeout(function() {
        console.log(j);
    }, 0)
}

它起作用,因为let声明了一个块范围局部变量。你不能使用var做同样的事情,因为它的范围是当前的执行上下文。

或者您可以使用包装函数:

for(var i=0; i<3; i++) {
    (function(j) {
        setTimeout(function() {
            console.log(j);
        }, 0)
    })(i);
}

for(var i=0; i<3; i++) {
    (function() {
        var j = i;
        setTimeout(function() {
            console.log(j);
        }, 0)
    })();
}

但是最好将它移到循环之外,以避免在每次迭代时重新创建它:

function f(j) {
    setTimeout(function() {
        console.log(j);
    }, 0)
}
for(var i=0; i<3; i++) {
    f(i);
}