What is wrong with my javascript scope?
以下内容每次都会提醒2。
1 2 3 4 5 6 7 8 9 10
| function timer() {
for (var i = 0; i < 3; ++i) {
var j = i;
setTimeout(function () {
alert(j);
}, 1000);
}
}
timer(); |
难道var j = i;不应该把j设定在设定的单独范围内吗?
如果我这样做:
1 2 3 4 5 6 7 8 9 10 11
| function timer() {
for (var i = 0; i < 3; ++i) {
(function (j) {
setTimeout(function () {
alert(j);
}, 1000);
})(i);
}
}
timer(); |
它会像应该的那样提醒0、1、2。
我有什么东西不见了吗?
- "像它应该的那样"-你是说"像我想要的那样"?:)
- @我想是吧哈哈
- 你只是不见了,那个javascript被打破了,一周前我也有过同样的WTF时刻……:。(
- @视力不太好。我知道他们是怎么工作的。我在想,为什么在这种情况下他们不能像他们应该做的那样工作。
- @Neal,我个人认为j不是在setTimeout的范围内初始化的,而是在timer的范围内初始化的,而在第二个例子中,你创建了一个匿名函数,通过i在关闭范围内隐式初始化j。这将创建并执行3个功能块,一次设置3个超时。
- @环的视觉没有自己的范围?
- @尼尔不,因为循环没有自己的范围。在下一个版本的javascript中,将有一个let而不是var来修复这个问题。
- @neal no,在for循环中定义的变量与for循环所处的范围相同。参考:developer.mozilla.org/en-us/docs/web/javascript/reference/&hellip;。
- 让我吃惊的是,有人回答了1000多个javascript/jquery问题,却不知道变量范围在语言中是如何工作的。
- @蓝天对我也有影响。所以我问了这个问题。我每天都学到新东西
- @Filmor和您不知道闭包是该语言的一个非常强大的功能。;-)
- @robg javascript是一种花括号语言,我所知道的所有其他花括号语言都使用块范围,所以这对我来说是完全意外的。这与闭包无关,在本例中,imho滥用闭包来重新获得块范围。
- @Filmor似乎更像是一个封闭的问题,而不是不理解变量范围。我不认为缺少块范围是一个问题,如果有人理解闭包,他们应该知道如何修复它。无论如何,Ed6似乎可以在不中断当前闭包使用的情况下提供函数和块范围。不过,要在普通网络上使用它还需要很长时间。
javascript具有功能范围。这意味着
1 2 3
| for(...) {
var j = i;
} |
等于
1 2 3 4
| var j;
for(...) {
j = i;
} |
事实上,这就是JavaScript编译器实际处理此代码的方式。当然,这会导致你的小"诡计"失败,因为j会在setTimeout中的函数被调用之前递增,也就是说,j现在与i没有什么不同,它只是一个具有相同作用域的别名。
如果Javascript有块范围,那么您的技巧就可以了,因为j在每次迭代中都是一个新的变量。
您需要做的是创建一个新的作用域:
1 2 3 4 5 6
| for(var i = ...) {
(function (j) {
// you can safely use j here now
setTimeout(...);
})(i);
} |
- 有没有办法阻止代码这样定义它?
- 刚刚在中编辑了一个伪代码解决方案。
- 哈哈,这基本上就是我在手术结束后所做的。愚蠢的黑客
- @尼尔,你把最后一部分编辑进你的问题里了,是吗?因为如果没有,我一定是真的看不到写我的答案时没有看到它。
- @英戈布&252;RK不。一直都在那里。这就是我问题的全部要点…如果只有这样做的话。
- 哦……然后。我该睡觉了。好吧,至少你(希望如此?)解释一下为什么另一种方法不起作用。
- 赞成。。。我没有意识到JavaScript没有块范围。奇怪的。每天学习新事物。
IIFE是一个功能工厂:
1 2 3 4 5 6 7 8 9 10 11 12 13
| function timer() {
for (var i = 0; i < 3; ++i) {
setTimeout(createTimerCallback(i), 1000);
}
}
function createTimerCallback(i) {
return function() {
alert(i);
};
}
timer(); |
也就是说,这是javascript标记中最常见的问题之一。见:
- 循环内的javascript闭包——简单的实践示例
- javascript臭名昭著的循环问题?
- 哦!!为什么javascript让你做这些事情?
- 这不是黑客行为,只是范围在这种语言中的工作方式。
- 它没有什么不好的地方:函数作用域和块作用域是一个简单的语言特性,会产生这样的效果。它只是感觉像很多其他语言都有阻止范围,这将阻止这种行为。
另一种方法是使用(通常被滥用)关键字with:
1 2 3 4 5 6 7 8 9 10 11
| function timer() {
for (var i = 0; i < 3; ++i) {
with({j: i}) {
setTimeout(function () {
alert(j);
}, 1000);
}
}
}
timer(); |
它创建了一个与函数类似的新范围,但没有笨拙的语法。我在这里第一次看到:javascript的"with"语句有合法的用途吗?
- 我喜欢你给出一个with的例子,但是它没有创建一个"类似函数"的范围。存在着重要的差异,导致其从"严格模式"中被移除。
- @然后是蓝天伪镜。在实际应用中,大多数情况下,它的行为都是这样的。