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

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


当前回答

使用封闭结构,这将减少额外的for循环。您可以在单个for循环中执行此操作:

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

其他回答

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

这是异步代码中经常遇到的问题,变量i是可变的,在调用函数时,使用i的代码将被执行,并且i将变为其最后一个值,因此意味着在循环中创建的所有函数都将创建一个闭包,i将等于3(for循环的上限+1)。

解决此问题的一个方法是创建一个函数,该函数将为每次迭代保存i的值,并强制复制i(因为它是一个原语,如果有帮助,请将其视为快照)。

还有另一个解决方案:不要创建另一个循环,只需将this绑定到返回函数。

var函数=[];函数createFunc(i){返回函数(){console.log('我的值:'+i)//i的对数值。}.调用(this);}对于(var i=1;i<=5;i++){//5函数funcs[i]=创建Func(i);//调用createFunc()i=5次}

通过绑定它,也解决了问题。

通过ES6的新功能,可以管理块级范围:

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

OP问题中的代码替换为let而不是var。

最简单的解决方案是,

而不是使用:

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

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

警报“2”3次。这是因为在for循环中创建的匿名函数共享相同的闭包,并且在该闭包中,i的值是相同的。使用此选项可防止共享关闭:

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

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

这背后的想法是,用IIFE(立即调用函数表达式)封装for循环的整个主体,并将new_i作为参数传递,并将其捕获为i。由于匿名函数是立即执行的,因此匿名函数内部定义的每个函数的i值都不同。

此解决方案似乎适合任何此类问题,因为它需要对受此问题困扰的原始代码进行最小的更改。事实上,这是故意的,根本不应该是一个问题!