var函数=[];//让我们创建3个函数对于(变量i=0;i<3;i++){//并将其存储在funcs中funcs[i]=函数(){//每个都应该记录其值。console.log(“我的值:”,i);};}对于(变量j=0;j<3;j++){//现在让我们逐一查看函数[j]();}

它输出以下内容:

我的价值:3我的价值:3我的价值:3

而我希望它输出:

我的值:0我的价值:1我的价值:2


当使用事件侦听器导致函数运行延迟时,也会出现同样的问题:

var buttons=document.getElementsByTagName(“button”);//让我们创建3个函数for(var i=0;i<buttons.length;i++){//作为事件侦听器buttons[i].addEventListener(“单击”,函数(){//每个都应该记录其值。console.log(“我的值:”,i);});}<按钮>0</按钮><br/><button>1</button><br/><按钮>2</按钮>

…或异步代码,例如使用Promises:

//一些异步等待函数const wait=(ms)=>new Promise((解析,拒绝)=>setTimeout(解析,ms));对于(变量i=0;i<3;i++){//一旦每个承诺得到解决,就记录“i”。等待(i*100)。然后(()=>console.log(i));}

在for in和for of循环中也很明显:

常量arr=[1,2,3];常量fns=[];用于(arr中的变量i){fns.push(()=>console.log(“index:”,i));}对于(arr的var v){fns.push(()=>console.log(“值:”,v));}for(arr常量){var obj={number:n};//或新的MyLibObject({…})fns.push(()=>console.log(“n:”,n,“|”,“obj:”,JSON.stringify(obj)));}for(fns的var f){f();}

这个基本问题的解决方案是什么?


当前回答

我更喜欢使用forEach函数,它有自己的闭包,可以创建伪范围:

var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: " + i);
    };
});

for (var j = 0; j < 3; j++) {
    funcs[j](); // 0, 1, 2
}

这看起来比其他语言的范围更丑陋,但IMHO比其他解决方案更不可怕。

其他回答

只需将var关键字更改为let。

var是函数范围的。

let是块范围的。

当您开始编写代码时,for循环将迭代并将i的值赋值为3,在整个代码中该值将保持为3。我建议您阅读更多关于节点中作用域的信息(let、var、const和其他)

funcs = [];
for (let i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] =async function() {          // and store them in funcs
    await console.log("My value: " + i); // each should log its value.
  };
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

虽然这个问题已经过时并得到了解答,但我还有另一个相当有趣的解决方案:

var funcs = [];

for (var i = 0; i < 3; i++) {     
  funcs[i] = function() {          
    console.log("My value: " + i); 
 };
}

for (var i = 0; i < 3; i++) {
  funcs[i]();
}

变化很小,几乎很难看出我做了什么。我将第二个迭代器从j切换到I。这会及时刷新I的状态,从而获得所需的结果。我这样做是偶然的,但考虑到以前的答案,这是有道理的。

我写这篇文章是为了指出这一微小但非常重要的区别。希望这有助于为像我这样的其他学习者澄清一些困惑。

注:我没有分享这个,因为我认为这是正确的答案。这是一个在某些情况下可能会破裂的解决方案。事实上,我很惊讶它真的有效。

直到ES5,这个问题只能用闭包来解决。

但现在在ES6中,我们有块级范围变量。将var更改为let in first for循环将解决问题。

var函数=[];for(假设i=0;i<3;i++){//让我们创建3个函数funcs[i]=function(){//并将它们存储在funcs中console.log(“我的值:”+i);//每个都应该记录其值。};}对于(变量j=0;j<3;j++){函数[j]();//现在让我们逐一查看}

我更喜欢使用forEach函数,它有自己的闭包,可以创建伪范围:

var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: " + i);
    };
});

for (var j = 0; j < 3; j++) {
    funcs[j](); // 0, 1, 2
}

这看起来比其他语言的范围更丑陋,但IMHO比其他解决方案更不可怕。

Try:

var函数=[];对于(变量i=0;i<3;i++){funcs[i]=(函数(索引){返回函数(){console.log(“我的值:”+索引);};}(i) );}对于(变量j=0;j<3;j++){函数[j]();}

编辑(2014):

就我个人而言,@Aust最近关于使用.bind的回答是目前最好的解决方法。当您不需要或不想使用bind的thisArg时,也可以使用破折号/下划线的_.partial。