为什么在for循环中声明的变量被更新而不是在每次迭代时重新创建?

时间:2015-12-09 23:32:56

标签: javascript loops asynchronous closures

这是一段代码:

var data = [ 'data1', 'data2', 'data3' ];

for (var i = 0; i < data.length; i++) {
    var x = data[i];
    setTimeout(function() {
        console.log(x);
    }, i * 100);
}

输出是'data3'的3倍。我的问题是为什么?

据我所知,循环结束后将调用所有3个日志方法,变量i将等于3,但是!

我在循环中定义了x变量。我希望在每次循环迭代时,可以在本地重新创建此变量,以便在调用每个console.log(x)方法时,它们引用存储在内存中的3个不同变量。

但它看起来像代码:

var x = data[i];

更新相同的变量,而不是重新创建它。

有人可以解释这种行为吗?

1 个答案:

答案 0 :(得分:2)

使用var声明的变量具有函数范围。这意味着整个函数中只有一个变量具有该名称。 var声明的位置无关紧要。函数声明隐式“提升”到函数的顶部,是整个函数可用的单个变量。

您的代码与此相同,它说明了为什么x所有setTimeout()次呼叫共享var data = [ 'data1', 'data2', 'data3' ]; var x; for (var i = 0; i < data.length; i++) { x = data[i]; setTimeout(function() { console.log(x); }, i * 100); } 只有一个实例:

x

您可以通过在适当的位置引入一个函数来解决您的问题,以便在其自己的函数范围内唯一捕获var data = [ 'data1', 'data2', 'data3' ]; for (var i = 0; i < data.length; i++) { (function(x) { setTimeout(function() { console.log(x); }, i * 100); )}(data[i]); } 的每个值:

let

在最新的ES6中,变量可以使用let声明,然后它们将具有您想要的块范围。因此,在最新的浏览器或node.js中,您可以使用var data = [ 'data1', 'data2', 'data3' ]; for (var i = 0; i < data.length; i++) { let x = data[i]; setTimeout(function() { console.log(x); }, i * 100); } ,它将具有所需的块范围:

function stopTimer() {
clearTimeout(timerID);
timerID = null;
timerObj.value = save;

var theBody = document.getElementById("theBody");
setTimeout(function () { theBody.className = "white"; }, 0);
setTimeout(function () { theBody.className = "red"; }, 250);
setTimeout(function () { theBody.className = "white"; }, 500);
setTimeout(function () { theBody.className = "red"; }, 750);
setTimeout(function () { theBody.className = "white"; }, 1000);
setTimeout(function () { theBody.className = "red"; }, 1250);
setTimeout(function () { theBody.className = "white"; }, 1500);
setTimeout(function () { theBody.className = "red"; }, 1750);
setTimeout(function () { theBody.className = "white"; }, 2000);
setTimeout(function () { theBody.className = "white"; }, 2250);
setTimeout(function () { theBody.className = "red"; }, 2500);
setTimeout(function () { theBody.className = "white"; }, 2750);
setTimeout(function () { theBody.className = "red"; }, 3000);
setTimeout(function () { theBody.className = "white"; }, 3250);
setTimeout(function () { theBody.className = "red"; }, 3500);
setTimeout(function () { theBody.className = "white"; }, 3750);
setTimeout(function () { theBody.className = "red"; }, 4000);
setTimeout(function () { theBody.className = "white"; }, 4250);

var audio = document.createElement("audio");
audio.src = "alarm.mp3";
theBody.appendChild(audio);

audio.setAttribute("id", "audio");
audio.play();
setTimeout(function () {
    audio.pause();
}, 6000);``