为什么括号表示法不起作用?

时间:2016-04-27 08:02:31

标签: javascript settimeout

我正在编写一个函数来触发一系列点击处理程序,它们之间有一定的时间间隔。

function play(step){
    var buttons = document.querySelectorAll('.age');
    buttons[0].click();
    for (var i = 1; i < buttons.length; i++){
        setTimeout(function(b){
            b.click();
        }, 300 + (step*i), buttons[i]);
    }
}

上述功能按预期工作,但是,以下版本对我来说似乎应该是等效的,不起作用:

function play(step){
    var buttons = document.querySelectorAll('.age');
    buttons[0].click();
    for (var i = 1; i < buttons.length; i++){
        setTimeout(function(b){
            console.log(i);
            console.log(b);
            b[i].click();
        }, 300 + (step*i), buttons);
    }
}

我收到以下错误:

 TypeError: Cannot read property 'click' of undefined

检查后,我发现console.log(i)正在打印6.所以,显然,直到循环结束后才发生属性访问,这解释了错误!但到底是怎么回事?我对javascript比较新,但是这个行为是匿名函数作为闭包的结果吗?这对我来说听起来不是正确的解释。是因为setTimeout延迟了对匿名函数的评估吗?

ETA:

我做了一个实验:

        function sleep(ms){
            var current = new Date().getTime();
            while (current + ms >= new Date().getTime()){};
        }
        function play(step){
            var buttons = document.querySelectorAll('.age');
            buttons[0].click();
            for (var i = 1; i < buttons.length; i++){
                setTimeout(function(b){
                    console.log(b);
                    console.log(i);
                    b[i].click();
                }, 300 + (step*i), buttons);
                sleep(1000);

            }
        }

当我运行play(200)时,我收到相同的错误消息,所以sleep(1000)应该有足够的时间确保在第一次超时之前循环没有退出,不是吗?

1 个答案:

答案 0 :(得分:2)

因为for循环中的变量在每次迭代中都是相同的(相同的引用),并且当setTimeout运行时,for循环已经结束,所以i变量将是循环的最后一个值,到修复此问题,您可以使用i变量创建一个闭包:

function play(step){
    var buttons = document.querySelectorAll('.age');
    buttons[0].click();
    for (var i = 1; i < buttons.length; i++){
        (function(i) {
            setTimeout(function(b){
                console.log(i);
                console.log(b);
                b[i].click();
            }, 300 + (step*i), buttons);
        })(i);
    }
}