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

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


当前回答

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

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

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

其他回答

让我们利用新功能。因此,i停止成为闭包的变量,并成为文本的一部分:

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

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

我们将检查,当您声明var并让逐一地。

案例1:使用var

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

现在,按F12打开铬控制台窗口并刷新页面。在数组中每3个函数展开一次。您将看到一个名为[[Scopes]]的属性。展开该属性。你会看到一个名为“Global”的数组对象,展开该对象。您将发现对象中声明的属性“i”具有值3。

结论:

当您在函数外部使用“var”声明变量时,它将变为全局变量(您可以通过键入i或window.i。它将返回3)。除非调用功能。调用函数时,console.log(“My value:”+i)从其Global对象中获取值,并显示后果

CASE2:使用let

现在将“var”替换为“let”

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

做同样的事情,去观察镜。现在您将看到两个对象“块”和“全局”。现在展开Block对象将看到这里定义了“i”,奇怪的是,对于每个函数,如果i不同(0,1,2),则其值是不同的。

结论:

当您在函数外部但在循环内部使用“let”声明变量时,此变量将不是全局变量变量,它将成为块级变量,仅可用于同一函数。这就是为什么,我们调用函数时,每个函数的i值都不同。

有关更紧密的工作原理的更多信息,请浏览精彩的视频教程https://youtu.be/71AtaJpJHw0

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

在阅读了各种解决方案之后,我想补充一点,这些解决方案之所以有效,是因为依赖于范围链的概念。这是JavaScript在执行期间解析变量的方式。

每个函数定义形成一个范围,该范围由所有本地var声明的变量及其参数。如果我们在另一个(外部)函数中定义了内部函数形成一条链,并将在执行过程中使用当函数被执行时,运行时通过搜索作用域链来评估变量。如果在链的某个点上可以找到一个变量,它将停止搜索并使用它,否则将继续搜索,直到到达属于window的全局范围。

在初始代码中:

funcs = {};
for (var i = 0; i < 3; i++) {         
  funcs[i] = function inner() {        // function inner's scope contains nothing
    console.log("My value: " + i);    
  };
}
console.log(window.i)                  // test value 'i', print 3

当执行funcs时,作用域链将是函数inner->global。由于在函数内部找不到变量i(既没有使用var声明,也没有作为参数传递),因此它继续搜索,直到最终在全局范围(即window.i)中找到i的值。

通过将其包装在外部函数中,要么像harto那样显式定义助手函数,要么像Bjorn那样使用匿名函数:

funcs = {};
function outer(i) {              // function outer's scope contains 'i'
  return function inner() {      // function inner, closure created
   console.log("My value: " + i);
  };
}
for (var i = 0; i < 3; i++) {
  funcs[i] = outer(i);
}
console.log(window.i)          // print 3 still

当执行funcs时,现在作用域链将是函数inner->函数outer。这一次,可以在for循环中执行3次的外部函数的作用域中找到i,每次都有正确绑定的值i。当内部执行时,它不会使用window.i的值。

更多详情请点击此处它包括在循环中创建闭包的常见错误,以及我们需要闭包的原因和性能考虑。

这个问题已经有很多有效答案了。但使用功能方法的并不多。这里有一个使用forEach方法的替代解决方案,它可以很好地处理回调和闭包:

设arr=[1,2,3];

让myFunc=(val,index)=>{console.log('val:'+val+'\nindex:'+index);};

arr.forEach(myFunc);