我的javascript范围有什么问题?

时间:2013-11-11 19:16:50

标签: javascript scope closures

每次都会发出以下警告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();

它会提醒012

我有什么遗失的吗?

3 个答案:

答案 0 :(得分:29)

Javascript具有功能范围。这意味着

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

相当于

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

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

如果Javascript具有块范围,那么你的技巧就可以了,因为j在每次迭代中都是一个新变量。

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

for(var i = ...) {
    (function (j) {
        // you can safely use j here now
        setTimeout(...);
    })(i);
}

答案 1 :(得分:4)

IIFE的替代方案是功能工厂:

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

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

timer();

这就是说,这是javascript标签中最常见的问题之一。参见:

答案 2 :(得分:2)

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

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

timer();

它创建了一个像函数一样的新范围,但没有笨拙的语法。我第一次看到它:Are there legitimate uses for JavaScript's “with” statement?