【问题标题】:What is wrong with my javascript scope? [duplicate]我的 javascript 范围有什么问题? [复制]
【发布时间】:2013-11-23 17:03:21
【问题描述】:

以下提醒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

我有什么遗漏吗?

【问题讨论】:

  • "like it should" - 你的意思是“like I want it to”吗? :)
  • 你只是想念,Javascript 坏得要命,大约一周前我有完全相同的 WTF 时刻...... :(
  • @VisioN 不是。我知道他们是如何工作的。我想知道为什么他们在这种情况下不能正常工作。
  • @Neal 好吧,我个人看到j 没有在setTimeout 的范围内初始化,而是在timer 函数的范围内初始化,而在第二个示例中,您创建了一个匿名函数,在哪里传递i,在闭包范围内隐式初始化j。这将创建并执行 3 个功能块,一次设置 3 个超时。
  • 令我惊讶的是,回答了 1000 多个 JavaScript/jQuery 问题的人不知道变量范围在语言中是如何工作的。

标签: javascript scope closures


【解决方案1】:

另一种方法是使用(通常被滥用的)关键字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?

【讨论】:

  • 我喜欢你给出了一个with 的例子,但它并没有创建一个“像函数一样”的范围。存在重要差异,导致其从“严格模式”中移除。
  • @BlueSkies 伪范围,然后。在实际使用中,大部分情况下,它的行为就像它一样。
【解决方案2】:

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);
}

【讨论】:

  • 有什么办法可以防止代码这样定义吗?
  • 刚刚编辑了一个伪代码解决方案。
  • 哈哈,这基本上就是我在我的 OP 结束时所做的......愚蠢的黑客。
  • @Neal 您将最后一部分编辑到您的问题中,不是吗?因为如果没有,我一定是真的瞎了眼,写答案的时候没看到。
  • @IngoBürk 不。那一直在那里。这就是我的问题的全部意义......如果这是唯一的方法。
【解决方案3】:

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

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

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

timer();

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

【讨论】:

  • 太老套了!!为什么 javascript 会让你做这些事情?
  • 这不是 hacky,这只是 scope 在这种语言中的工作方式。
  • 这没什么好奇怪的:函数作用域与块作用域是一个简单的语言特性,它带有这样的效果。只是感觉很奇怪,因为许多其他语言都有阻止这种行为的块作用域。
猜你喜欢
  • 2020-03-12
  • 2017-07-03
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多