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();}
这个基本问题的解决方案是什么?
我们将检查,当您声明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
您可以为数据列表(如查询js(*))使用声明性模块。在这些情况下,我个人认为声明性方法不太令人惊讶
var funcs = Query.range(0,3).each(function(i){
return function() {
console.log("My value: " + i);
};
});
然后,您可以使用第二个循环并获得预期的结果,也可以这样做
funcs.iterate(function(f){ f(); });
(*)我是query js的作者,因此倾向于使用它,所以不要将我的话作为仅针对声明性方法的所述库的建议:)