62

2每次都会发出以下警报。

function timer() {
    for (var i = 0; i < 3; ++i) {
        var j = i;
        setTimeout(function () {
            alert(j);
        }, 1000);
    }
}

timer();

不应该var j = i;将其设置j为 setTimeout 的单个范围吗?

而如果我这样做:

function timer() {
    for (var i = 0; i < 3; ++i) {
        (function (j) {
            setTimeout(function () {
                alert(j);
            }, 1000);
        })(i);
    }
}

timer();

它会提醒0, 12就像它应该的那样。

有什么我想念的吗?

4

3 回答 3

35

Javascript具有函数范围。这意味着

for(...) {
    var j = i;
}

相当于

var j;
for(...) {
    j = i;
}

事实上,这就是 Javascript 编译器实际处理此代码的方式。而且,当然,这会导致您的小“技巧”​​失败,因为j将在函数 insetTimeout被调用之前递增,即j现在实际上并没有做任何与 不同的事情i,它只是具有相同范围的别名。

如果 Javascript 具有块作用域,那么您的技巧就会奏效,因为j在每次迭代中都是一个新变量。

您需要做的是创建一个新范围:

for(var i = ...) {
    (function (j) {
        // you can safely use j here now
        setTimeout(...);
    })(i);
}
于 2013-11-11T19:18:46.057 回答
6

IIFE 的替代方案是函数工厂:

function timer() {
    for (var i = 0; i < 3; ++i) {
        setTimeout(createTimerCallback(i), 1000);
    }
}

function createTimerCallback(i) {
    return function() {
       alert(i);
    };
}

timer();

话虽这么说,这是 javascript 标记中被问到最多的问题之一。看:

于 2013-11-11T19:26:10.363 回答
3

另一种方法是使用(通常被滥用的)关键字with

function timer() {
    for (var i = 0; i < 3; ++i) {
        with({j: i}) {
            setTimeout(function () {
                alert(j);
            }, 1000);
        }
    }
}

timer();

它像函数一样创建了一个新的作用域,但没有笨拙的语法。我第一次看到它是在这里: JavaScript 的“with”语句有合法用途吗?

于 2013-11-11T19:41:46.887 回答