我的JavaScript范围有什么问题?

这个问题已经在这里有了答案:

  • JavaScript闭包如何工作?                                     86个回答
  • 循环内的JavaScript闭合–简单的实际示例                                     44个答案

每次以下警报0

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

timer();

0是否不应该将1设置为setTimeout的各个范围?

而如果我这样做:

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

timer();

它会像它应该警告012一样。

我有什么想念的吗?

Neal asked 2020-02-13T22:53:27Z
3个解决方案
30 votes

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);
}
Ingo Bürk answered 2020-02-13T22:54:04Z
5 votes

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

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

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

timer();

话虽如此,这是javascript标记中最常见的问题之一。 看到:

  • 循环内的JavaScript闭合–简单的实际示例
  • JavaScript臭名昭著的循环问题?
bfavaretto answered 2020-02-13T22:54:37Z
2 votes

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

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

timer();

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

Izkata answered 2020-02-13T22:55:01Z
translate from https://stackoverflow.com:/questions/19914077/what-is-wrong-with-my-javascript-scope