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

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


当前回答

使用立即调用函数表达式,这是封闭索引变量的最简单和最易读的方法:

对于(变量i=0;i<3;i++){(函数(索引){console.log('iterator:'+索引);//现在您还可以在这里循环ajax调用//而不会丢失迭代器值:$.ajax({});})(i) ;}

这将迭代器i发送到我们定义为索引的匿名函数中。这将创建一个闭包,其中变量i将被保存以供以后在IIFE中的任何异步功能中使用。

其他回答

使用let(阻塞范围)代替var。

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

OP显示的代码的主要问题是,直到第二个循环才读取i。为了演示,想象一下在代码中看到一个错误

funcs[i] = function() {            // and store them in funcs
    throw new Error("test");
    console.log("My value: " + i); // each should log its value.
};

在执行funcs[someIndex]()之前,错误实际上不会发生。使用相同的逻辑,应该很明显,直到这一点,i的值也不会被收集。一旦原始循环完成,i++将i值设为3,这导致条件i<3失败,循环结束。此时,i为3,因此当使用funcs[someIndex]()时,每次计算i时,i均为3。

要克服这一点,必须在遇到i时对其求值。注意,这已经以funcs[i]的形式发生(其中有3个唯一索引)。有几种方法可以获取此值。一种是将其作为参数传递给函数,该函数在这里已经以多种方式显示。

另一种选择是构造一个函数对象,它将能够在变量上关闭。这样就可以完成

jsFiddle演示

funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: " + closedVariable); 
    };
};

另一种尚未提及的方法是使用Function.prototype.bind

var funcs={};对于(变量i=0;i<3;i++){funcs[i]=函数(x){console.log('我的值:'+x);}.绑定(this,i);}对于(变量j=0;j<3;j++){函数[j]();}

更新

正如@squit和@mekdev所指出的,通过首先在循环外创建函数,然后在循环内绑定结果,可以获得更好的性能。

函数日志(x){console.log('我的值:'+x);}var函数=[];对于(变量i=0;i<3;i++){funcs[i]=log.bind(this,i);}对于(变量j=0;j<3;j++){函数[j]();}

为什么不在创建第一个(也是唯一的)循环之后,简单地调用它们,例如:

 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.
    };
    funcs[i]();// and now let's run each one to see
    }

许多解决方案看起来都是正确的,但他们没有提到它叫做Currying,这是一种针对类似情况的函数式编程设计模式。根据浏览器的不同,比绑定快3-10倍。

var funcs = [];
for (var i = 0; i < 3; i++) {      // let's create 3 functions
  funcs[i] = curryShowValue(i);
}
for (var j = 0; j < 3; j++) {
  funcs[j]();                      // and now let's run each one to see
}

function curryShowValue(i) {
  return function showValue() {
    console.log("My value: " + i);
  }
}

查看不同浏览器中的性能增益。