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

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


当前回答

  asyncIterable = [1,2,3,4,5,6,7,8];

  (async function() {
       for await (let num of asyncIterable) {
         console.log(num);
       }
    })();

其他回答

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

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

Try:

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

编辑(2014):

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

首先,了解此代码的错误:

var funcs = [];
for (var 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 (var j = 0; j < 3; j++) {
    funcs[j]();                        // and now let's run each one to see
}

这里,当funcs[]数组初始化时,i递增,funcs数组初始化,func数组的大小变为3,因此i=3,。现在,当调用funcs[j]()时,它再次使用变量i,变量i已经增加到3。

现在要解决这个问题,我们有很多选择。以下是其中两个:

我们可以用let初始化i,或者用let初始化一个新的变量索引,并使其等于i。因此,当进行调用时,将使用索引,其范围将在初始化后结束。对于调用,索引将再次初始化:var函数=[];对于(var i=0;i<3;i++){设索引=i;funcs[i]=函数(){console.log(“我的值:”+索引);};}对于(变量j=0;j<3;j++){函数[j]();}其他选项可以是引入一个返回实际函数的tempFunc:var函数=[];函数tempFunc(i){返回函数(){console.log(“我的值:”+i);};}对于(var i=0;i<3;i++){funcs[i]=tempFunc(i);}对于(变量j=0;j<3;j++){函数[j]();}

随着ES6现在得到广泛支持,这个问题的最佳答案已经改变。ES6为这种情况提供了let和const关键字。我们可以使用let来设置一个循环范围变量,而不是使用闭包:

var函数=[];对于(设i=0;i<3;i++){funcs[i]=函数(){console.log(“我的值:”+i);};}

然后,val将指向一个特定于循环特定转弯的对象,并将返回正确的值,而无需附加闭包符号。这显然大大简化了这个问题。

const类似于let,但有一个额外的限制,即变量名不能在初始赋值后反弹到新引用。

现在,针对最新版本浏览器的浏览器提供了浏览器支持。const/let目前在最新的Firefox、Safari、Edge和Chrome中受支持。Node也支持它,您可以利用Babel等构建工具在任何地方使用它。您可以在这里看到一个工作示例:http://jsfiddle.net/ben336/rbU4t/2/

此处的文档:

常量允许

不过,请注意,IE9-IE11和Edge 14之前的Edge支持以上错误(它们不会每次都创建一个新的i,所以上面的所有函数都会记录3,就像我们使用var时一样)。边缘14最终得到了正确的结果。

您可以为数据列表(如查询js(*))使用声明性模块。在这些情况下,我个人认为声明性方法不太令人惊讶

var funcs = Query.range(0,3).each(function(i){
     return  function() {
        console.log("My value: " + i);
    };
});

然后,您可以使用第二个循环并获得预期的结果,也可以这样做

funcs.iterate(function(f){ f(); });

(*)我是query js的作者,因此倾向于使用它,所以不要将我的话作为仅针对声明性方法的所述库的建议:)