Javascript函数范围[Javascript Essentials]
我在看Travis Tidwell的Javascript Essentials,他解释了这段代码:Javascript函数范围[Javascript Essentials],javascript,arrays,function,for-loop,anonymous-function,Javascript,Arrays,Function,For Loop,Anonymous Function,我在看Travis Tidwell的Javascript Essentials,他解释了这段代码: (function() { var messages = ['hello', 'there']; for (var i in messages) { setTimeout(function() { console.log(messages[i]); }, 10); }; })(); 它在控制台中重复“there”
(function() {
var messages = ['hello', 'there'];
for (var i in messages) {
setTimeout(function() {
console.log(messages[i]);
}, 10);
};
})();
它在控制台中重复“there”两次,但我仍然不明白确切的原因。有人能和我一起一步一步地看一下这段javascript吗?每次代码循环时,它都会设置一个事件处理程序,以便在经过10毫秒后,它会记录
消息[i]
的值
在这些超时超过10毫秒之前,i
的值已更改(通过for
循环)为1
(因为这是数组中最后的属性名称)
第一次超时输出消息[1]
,然后第二次超时输出消息[1]
消息中
i
设置为0
并设置超时i
设置为1
,并设置超时i
仍然是1
i
仍然是1
JavaScript具有函数作用域,而不像其他语言那样具有块作用域。因此,实际上只存在一个
i
变量。调用setTimeout
中的代码时,i
已设置为数组的最后一个索引
很快,在ECMAScript 6中,我们可以使用let
声明块作用域变量。请看这里:
在此之前,解决此行为的方法之一是为需要独立于其他变量的变量创建新函数:
(函数(){
var messages=['hello','there'];
for(消息中的var i){
(功能(当前索引){
setTimeout(函数(){
logToOutput(消息[currentIndex]);
}, 10);
})(i) );
};
})();
函数logToOutput(msg){
document.getElementById(“输出”).innerHTML+=msg+“
”;
}
请参阅:这是臭名昭著的循环内闭包问题。如果你搜索这个,你会发现大量的信息。本质上,这种行为的原因是for循环在超时触发时已经完成。此时,i
具有最后一个循环的值,用于两个超时。如果将“10”替换为“0”,为什么行为相同?@johnnyRose浏览器施加的最小超时值通常超过10毫秒。因为(a)强制执行最小超时,并且(b)JavaScript是单线程的,事件循环不寻找在运行另一个函数的过程中要处理的事件。@ J.NynRoSE作为概念证明来显示其他评论者所说的内容,请参见。打开控制台,看到在“there”消息之前有两条“loop iteration”消息…“但设置超时时,它没有存储i[0]计数,这真的很奇怪”-不,它没有。这是JavaScript的一个基本特性。创建函数不会立即创建每个变量的本地范围副本并冻结其值。该函数在使用前甚至不会查看i
。