JavaScript closure inside loops – simple practical example
1 2 3 4 5 6 7 8 9 | 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 } |
它输出:
My value: 3
My value: 3
My value: 3
但是我希望它输出:
My value: 0
My value: 1
My value: 2
当使用事件侦听器导致函数运行延迟时,也会出现同样的问题:
1 2 3 4 5 6 | var buttons = document.getElementsByTagName("button"); for (var i = 0; i < buttons.length; i++) { // let's create 3 functions buttons[i].addEventListener("click", function() { // as event listeners console.log("My value:" + i); // each should log its value. }); } |
1 2 3 | <button>0</button> <button>1</button> <button>2</button> |
…或异步代码,例如使用承诺:
1 2 3 4 5 6 | // Some async wait function const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms)); for(var i = 0; i < 3; i++){ wait(i * 100).then(() => console.log(i)); // Log `i` as soon as each promise resolves. } |
这个基本问题的解决方案是什么?
问题是,变量
您要做的是将每个函数中的变量绑定到函数外部一个独立的、不变的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 | var funcs = []; function createfunc(i) { return function() { console.log("My value:" + i); }; } for (var i = 0; i < 3; i++) { funcs[i] = createfunc(i); } for (var j = 0; j < 3; j++) { funcs[j](); // and now let's run each one to see } |
由于在javascript中没有块作用域(只有函数作用域),通过将函数创建包装到新函数中,可以确保"i"的值保持原样。
2015解决方案:foreach随着
1 2 3 4 5 6 7 8 | var someArray = [ /* whatever */ ]; // ... someArray.forEach(function(arrayElement) { // ... code code code for this one element someAsynchronousFunction(arrayElement, function() { arrayElement.doSomething(); }); }); |
其思想是,与
如果您正好在jquery中工作,那么
ecmascript 6(es6)引入了新的
1 2 3 4 5 | for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value:" + i); }; } |
不过,要注意,IE9-IE11和Edge Prior to Edge 14支持
尝试:
1 2 3 4 5 6 7 8 9 10 11 12 | var funcs = []; for (var i = 0; i < 3; i++) { funcs[i] = (function(index) { return function() { console.log("My value:" + index); }; }(i)); } for (var j = 0; j < 3; j++) { funcs[j](); } |
编辑(2014):
就我个人而言,我认为@aust最近关于使用
另一种尚未提及的方法是使用
1 2 3 4 5 6 7 8 9 | var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = function(x) { console.log('My value: ' + x); }.bind(this, i); } for (var j = 0; j < 3; j++) { funcs[j](); } |
更新
正如@squint和@mekdev所指出的,首先在循环外部创建函数,然后在循环内部绑定结果,可以获得更好的性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 | function log(x) { console.log('My value: ' + x); } var funcs = []; for (var i = 0; i < 3; i++) { funcs[i] = log.bind(this, i); } for (var j = 0; j < 3; j++) { funcs[j](); } |
使用立即调用的函数表达式,最简单、最易读的方法来包含索引变量:
1 2 3 4 5 6 7 8 9 | for (var i = 0; i < 3; i++) { (function(index) { console.log('iterator: ' + index); //now you can also loop an ajax call here //without losing track of the iterator value: $.ajax({}); })(i); } |
它将迭代器
参加聚会有点晚,但我今天正在研究这个问题,注意到许多答案并没有完全解决JavaScript如何处理范围的问题,这本质上就是归根结底。
正如其他许多人提到的,问题是内部函数引用的是同一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | //overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '<p> ' + msg + ' </p>';}; var funcs = {}; for (var i = 0; i < 3; i++) { var ilocal = i; //create a new local variable funcs[i] = function() { console.log("My value:" + ilocal); //each should reference its own local variable }; } for (var j = 0; j < 3; j++) { funcs[j](); } |
和以前一样,每个内部函数输出分配给
结果,这就是问题所在。每个迭代都共享相同的范围,所以第一次迭代之后的每个迭代都只是覆盖
Important: JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script, and the effects of setting them persist beyond the block itself. In other words, block statements do not introduce a scope. Although"standalone" blocks are valid syntax, you do not want to use standalone blocks in JavaScript, because they don't do what you think they do, if you think they do anything like such blocks in C or Java.
再次强调:
JavaScript does not have block scope. Variables introduced with a block are scoped to the containing function or script
我们可以通过在每次迭代中声明之前检查
1 2 3 4 5 6 7 8 9 10 | //overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '<p> ' + msg + ' </p>';}; var funcs = {}; for (var i = 0; i < 3; i++) { console.log(ilocal); var ilocal = i; } |
这就是为什么这个bug如此棘手的原因。即使您正在重新定义一个变量,javascript也不会抛出错误,JSlint甚至不会抛出警告。这也是解决这一问题的最佳方法是利用闭包的原因,闭包本质上是指在JavaScript中,内部函数可以访问外部变量,因为内部范围"包含"了外部范围。
这也意味着内部函数"保留"外部变量并使它们保持活动,即使外部函数返回。为了利用这个功能,我们创建并调用一个包装函数来创建一个新的作用域,在新的作用域中声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '<p> ' + msg + ' </p>';}; var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = (function() { //create a new scope using a wrapper function var ilocal = i; //capture i into a local var return function() { //return the inner function console.log("My value:" + ilocal); }; })(); //remember to run the wrapper function } for (var j = 0; j < 3; j++) { funcs[j](); } |
在包装函数内创建内部函数会给内部函数一个只有它可以访问的私有环境,即"闭包"。因此,每次调用包装器函数时,我们都会使用它自己的独立环境创建一个新的内部函数,以确保
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | //overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '<p> ' + msg + ' </p>';}; var funcs = {}; for (var i = 0; i < 3; i++) { funcs[i] = wrapper(i); } for (var j = 0; j < 3; j++) { funcs[j](); } //creates a separate environment for the inner function function wrapper(ilocal) { return function() { //return the inner function console.log("My value:" + ilocal); }; } |
更新
现在ES6已经成为主流,我们可以使用新的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //overwrite console.log() so you can see the console output console.log = function(msg) {document.body.innerHTML += '<p> ' + msg + ' </p>';}; var funcs = {}; for (let i = 0; i < 3; i++) { // use"let" to declare"i" funcs[i] = function() { console.log("My value:" + i); //each should reference its own local variable }; } for (var j = 0; j < 3; j++) { // we can use"var" here without issue funcs[j](); } |
看看现在有多简单!有关更多信息,请参阅此答案,我的信息基于此答案。
随着ES6得到了广泛的支持,这个问题的最佳答案已经改变了。ES6为这种具体情况提供了
1 2 3 4 5 6 | var funcs = []; for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value:" + i); }; } |
然后,
浏览器支持现在针对的是最新版本的浏览器。最新的火狐、Safari、Edge和Chrome目前支持
这里的医生:
- 康斯特
- 让
不过,要注意,IE9-IE11和Edge Prior to Edge 14支持
另一种说法是,函数中的
创建闭包时,
大多数其他答案都提供了解决方法,通过创建另一个不会改变值的变量。
我只是想加一个解释来解释清楚。我个人认为,要想找到一个解决办法,我会选择哈托的方法,因为从这里的答案来看,这是最不言自明的方法。发布的任何代码都可以工作,但是我会选择关闭工厂,而不是写一堆注释来解释为什么我要声明一个新变量(Freddy和1800),或者有奇怪的嵌入式关闭语法(Apphacker)。
您需要了解的是,javascript中变量的范围是基于函数的。这比在有块范围的地方说c有一个重要的区别,只需将变量复制到for中的一个就可以了。
将它包装在一个函数中,该函数的计算结果将返回类似于Apphacker的答案的函数,这将达到目的,因为变量现在具有函数范围。
还有一个let关键字而不是var,它允许使用块范围规则。在这种情况下,在for中定义一个变量可以做到这一点。也就是说,由于兼容性的原因,let关键字不是一个实用的解决方案。
1 2 3 4 5 6 7 8 9 10 | var funcs = {}; for (var i = 0; i < 3; i++) { let index = i; //add this funcs[i] = function() { console.log("My value:" + index); //change to the copy }; } for (var j = 0; j < 3; j++) { funcs[j](); } |
这是该技术的另一个变体,类似于bjorn(apphacker),它允许您在函数内部分配变量值,而不是将其作为参数传递,有时可能更清楚:
1 2 3 4 5 6 7 8 | for (var i = 0; i < 3; i++) { funcs[i] = (function() { var index = i; return function() { console.log("My value:" + index); } })(); } |
注意,无论使用什么技术,
这描述了在JavaScript中使用闭包的常见错误。
函数定义新环境考虑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function makeCounter() { var obj = {counter: 0}; return { inc: function(){obj.counter ++;}, get: function(){return obj.counter;} }; } counter1 = makeCounter(); counter2 = makeCounter(); counter1.inc(); alert(counter1.get()); // returns 1 alert(counter2.get()); // returns 0 |
每次调用
在循环中使用闭包是很困难的。
考虑:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | var counters = []; function makeCounters(num) { for (var i = 0; i < num; i++) { var obj = {counter: 0}; counters[i] = { inc: function(){obj.counter++;}, get: function(){return obj.counter;} }; } } makeCounters(2); counters[0].inc(); alert(counters[0].get()); // returns 1 alert(counters[1].get()); // returns 1 |
注意,
这是因为在循环的所有迭代中,可能出于性能原因,只有一个
解决方案是使用另一个助手函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | function makeHelper(obj) { return { inc: function(){obj.counter++;}, get: function(){return obj.counter;} }; } function makeCounters(num) { for (var i = 0; i < num; i++) { var obj = {counter: 0}; counters[i] = makeHelper(obj); } } |
这是因为直接分配了函数范围中的局部变量以及函数参数变量新副本一经输入。
有关详细讨论,请参阅javascript闭包陷阱和用法。
最简单的解决方案是,
而不是使用:
1 2 3 4 5 6 7 8 9 10 | 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循环中创建的匿名函数共享相同的闭包,并且在该闭包中,
1 2 3 4 5 6 7 8 9 10 11 12 | 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循环的整个主体,并将
这个解决方案似乎适合任何这样的问题,因为它需要对受此问题影响的原始代码进行最小的更改。事实上,这是按设计的,根本不应该是一个问题!
试试这个短点的
无阵列
没有额外的循环
< BR>
1 2 3 4 5 6 7 | for (var i = 0; i < 3; i++) { createfunc(i)(); } function createfunc(i) { return function(){console.log("My value:" + i);}; } |
http://jsfiddle.net/7p6en/
OP显示的代码的主要问题是,直到第二个循环,
1 2 3 4 | funcs[i] = function() { // and store them in funcs throw new Error("test"); console.log("My value:" + i); // each should log its value. }; |
在执行
为了克服这一点,您必须对遇到的
另一个选项是构造一个函数对象,它将能够关闭变量。那是完全可以实现的
1 2 3 4 5 6 | funcs[i] = new function() { var closedVariable = i; return function(){ console.log("My value:" + closedVariable); }; }; |
下面是一个使用
1 2 3 4 5 6 7 8 9 | var funcs = []; [0,1,2].forEach(function(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 } |
印刷品:
1
2
3 My value: 0
My value: 1
My value: 2
javascript函数在声明时"关闭"它们可以访问的作用域,并保留对该作用域的访问权,即使该作用域中的变量发生更改。
1 2 3 4 5 6 7 8 9 10 11 | var funcs = [] for (var i = 0; i < 3; i += 1) { funcs[i] = function () { console.log(i) } } for (var k = 0; k < 3; k += 1) { funcs[k]() } |
上面数组中的每个函数都在全局范围内关闭(全局的,因为这恰好是它们声明的范围)。
稍后,调用这些函数记录全局范围内
javascript函数关闭它们在其中声明的作用域,并保留对该作用域的访问权,即使该作用域内的变量值发生更改。
使用
1 2 3 4 5 6 7 8 9 10 11 | var funcs = [] for (let i = 0; i < 3; i += 1) { funcs[i] = function () { console.log(i) } } for (var k = 0; k < 3; k += 1) { funcs[k]() } |
(
在阅读了各种解决方案之后,我想补充一点,这些解决方案之所以有效,是因为依赖于范围链的概念。这是JavaScript在执行期间解析变量的方式。
- 每个函数定义形成一个由所有本地由
var 及其arguments 声明的变量。 - 如果我们在另一个(外部)函数中定义了内部函数,那么形成一个链,并将在执行期间使用
- 当函数被执行时,运行时通过搜索作用域链来评估变量。如果在链的某个点上可以找到一个变量,它将停止搜索并使用它,否则它将继续搜索,直到到达属于
window 的全局范围。
在初始代码中:
1 2 3 4 5 6 7 | 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 |
执行
通过将它包装在一个外部函数中,要么像harto那样显式定义一个助手函数,要么像bjorn那样使用一个匿名函数:
1 2 3 4 5 6 7 8 9 10 | 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 |
当执行
这里可以找到更多细节它包括在循环中创建闭包的常见错误,以及为什么需要闭包和性能考虑。
使用ES6块级的新功能,可以管理范围:
1 2 3 4 5 6 7 8 9 | 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问题中的代码替换为
我很惊讶还没有人建议使用
1 2 3 4 | [0,2,3].forEach(function(i){ console.log('My value:', i); }); // My value: 0 // My value: 2 // My value: 3 |
//编辑为使用
这个问题真的展示了JavaScript的历史!现在我们可以避免使用箭头函数进行块范围划分,并使用对象方法直接处理来自DOM节点的循环。
1 2 | const funcs = [1, 2, 3].map(i => () => console.log(i)); funcs.map(fn => fn()) |
1 2 3 4 | const buttons = document.getElementsByTagName("button"); Object .keys(buttons) .map(i => buttons[i].addEventListener('click', () => console.log(i))); |
1 2 3 | <button>0</button> <button>1</button> <button>2</button> |
首先,了解此代码有什么问题:
1 2 3 4 5 6 7 8 9 | 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 } |
这里初始化
为了解决这个问题,我们有很多选择。以下是其中两个:
我们可以用
1 2 3 4 5 6 7 8 9 10 | var funcs = []; for (var i = 0; i < 3; i++) { let index = i; funcs[i] = function() { console.log("My value:" + index); }; } for (var j = 0; j < 3; j++) { funcs[j](); } |
另一种选择是引入一个返回实际函数的
1 2 3 4 5 6 7 8 9 10 11 12 | var funcs = []; function tempFunc(i){ return function(){ console.log("My value:" + i); }; } for (var i = 0; i < 3; i++) { funcs[i] = tempFunc(i); } for (var j = 0; j < 3; j++) { funcs[j](); } |
We will check , what actually happens when you declare
var andlet
one by one.
案例1:使用EDOCX1[4]
1 2 3 4 5 6 7 8 | var funcs = []; for (var i = 0; i < 3; i++) { funcs[i] = function () { debugger; console.log("My value:" + i); }; } console.log(funcs); |
现在按F12打开Chrome控制台窗口并刷新页面。在数组中每3个函数展开一次。您将看到一个名为
结论:
案例2:使用let
现在用
1 2 3 4 5 6 7 8 | var funcs = []; for (let i = 0; i < 3; i++) { funcs[i] = function () { debugger; console.log("My value:" + i); }; } console.log(funcs); |
做同样的事情,到范围。现在您将看到两个对象:
结论:
当使用
有关如何更近距离工作的更多详细信息,请浏览精彩视频教程https://youtu.be/71atajpjhw0
原始示例不起作用的原因是您在循环中创建的所有闭包都引用了同一帧。实际上,在一个只有一个
使用闭包结构,这将减少额外的for循环。您可以在单个for循环中执行此操作:
1 2 3 4 5 6 | var funcs = []; for (var i = 0; i < 3; i++) { (funcs[i] = function() { console.log("My value:" + i); })(i); } |
我更喜欢使用
1 2 3 4 5 6 7 8 9 10 11 12 | var funcs = []; new Array(3).fill(0).forEach(function (_, i) { // creating a range funcs[i] = function() { // now i is safely incapsulated console.log("My value:" + i); }; }); for (var j = 0; j < 3; j++) { funcs[j](); // 0, 1, 2 } |
这看起来比其他语言的范围更丑,但我不觉得比其他解决方案更可怕。
1 2 3 4 5 6 7 8 9 | 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 } |
使用let(阻塞范围)而不是var。
1 2 3 4 5 6 7 8 9 | var funcs = []; for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value:" + i); }; } for (var j = 0; j < 3; j++) { funcs[j](); } |
您的代码不起作用,因为它的作用是:
1 2 3 4 5 6 7 8 9 10 11 12 | Create variable `funcs` and assign it an empty array; Loop from 0 up until it is less than 3 and assign it to variable `i`; Push to variable `funcs` next function: // Only push (save), but don't execute **Write to console current value of variable `i`;** // First loop has ended, i = 3; Loop from 0 up until it is less than 3 and assign it to variable `j`; Call `j`-th function from variable `funcs`: **Write to console current value of variable `i`;** // Ask yourself NOW! What is the value of i? |
现在的问题是,调用函数时,变量
您需要了解的是,在创建函数时,它们的代码都不会被执行,只会保存到以后。所以当稍后调用它们时,解释器执行它们并询问:"
因此,您的目标是首先将EDOCX1的值保存到函数,然后才将函数保存到
1 2 3 4 5 6 7 8 9 | var funcs = []; for (var i = 0; i < 3; i++) { // let's create 3 functions funcs[i] = function(x) { // and store them in funcs console.log("My value:" + x); // each should log its value. }.bind(null, i); } for (var j = 0; j < 3; j++) { funcs[j](); // and now let's run each one to see } |
这样,每个函数都有它自己的变量
这只是解决这个问题的多种方法之一。
许多解决方案似乎是正确的,但它们没有提到它被称为
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } } |
查看不同浏览器的性能提升。
您可以对数据列表(如查询JS(*)使用声明性模块。在这种情况下,我个人认为声明性方法不那么令人惊讶
1 2 3 4 5 | var funcs = Query.range(0,3).each(function(i){ return function() { console.log("My value:" + i); }; }); |
然后可以使用第二个循环并获得预期的结果,或者可以这样做
1 | funcs.iterate(function(f){ f(); }); |
(*)我是查询JS的作者,因此倾向于使用它,因此不要将我的话作为对所述库的建议,只针对声明性方法:)
还有另一种解决方案:不创建另一个循环,只需将
1 2 3 4 5 6 7 8 9 10 11 | var funcs = []; function createFunc(i) { return function() { console.log('My value: ' + i); //log value of i. }.call(this); } for (var i = 1; i <= 5; i++) { //5 functions funcs[i] = createFunc(i); // call createFunc() i=5 times } |
通过绑定它,也解决了问题。
这是异步代码经常遇到的一个问题,变量
解决方法是创建一个函数,该函数将为每个迭代保留
计数器是一个原语
让我们定义如下回调函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // **************************** // COUNTER BEING A PRIMITIVE // **************************** function test1() { for (var i=0; i<2; i++) { setTimeout(function() { console.log(i); }); } } test1(); // 2 // 2 |
超时完成后,它将为两者打印2。这是因为回调函数根据定义函数的词法范围访问值。
为了在定义回调时传递和保留值,我们可以创建一个闭包,以便在调用回调之前保留该值。可以按如下方式进行:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | function test2() { function sendRequest(i) { setTimeout(function() { console.log(i); }); } for (var i = 0; i < 2; i++) { sendRequest(i); } } test2(); // 1 // 2 |
特别的是"原语是通过值传递和复制的。因此,当定义了闭包时,它们会保留前一个循环的值。"
计数器是一个对象
因为闭包可以通过引用访问父函数变量,所以这种方法不同于原语。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // **************************** // COUNTER BEING AN OBJECT // **************************** function test3() { var index = { i: 0 }; for (index.i=0; index.i<2; index.i++) { setTimeout(function() { console.log('test3: ' + index.i); }); } } test3(); // 2 // 2 |
因此,即使为作为对象传递的变量创建了一个闭包,循环索引的值也不会被保留。这是为了表明对象的值不会被复制,而它们是通过引用访问的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | function test4() { var index = { i: 0 }; function sendRequest(index, i) { setTimeout(function() { console.log('index: ' + index); console.log('i: ' + i); console.log(index[i]); }); } for (index.i=0; index.i<2; index.i++) { sendRequest(index, index.i); } } test4(); // index: { i: 2} // 0 // undefined // index: { i: 2} // 1 // undefined |
只需将var关键字更改为let。
var是函数范围。
设为块范围。
当您开始编写代码时,for循环将迭代并将i的值赋给3,该值将在整个代码中保持为3。我建议您阅读更多关于节点中作用域的内容(let、var、const和其他)
1 2 3 4 5 6 7 8 9 | funcs = []; for (let i = 0; i < 3; i++) { // let's create 3 functions funcs[i] =async function() { // and store them in funcs await 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 } |
虽然这个问题由来已久,但我还有另一个相当有趣的解决方案:
1 2 3 4 5 6 7 8 9 10 11 | var funcs = []; for (var i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value:" + i); }; } for (var i = 0; i < 3; i++) { funcs[i](); } |
变化太小了,几乎很难看到我做了什么。我把第二个迭代器从j切换到i,这会及时刷新i的状态,从而得到想要的结果。我做这件事是偶然的,但考虑到以前的答案是有道理的。
我写这篇文章是为了指出这个小的,但非常重要的区别。希望这有助于消除像我这样的学习者的一些困惑。
注意:我没有分享这个,因为我认为这是正确的答案。这是一种片状溶液,在某些情况下可能会破裂。事实上,我很惊讶它真的有效。
1 2 3 4 5 6 7 | asyncIterable = [1,2,3,4,5,6,7,8]; (async function() { for await (let num of asyncIterable) { console.log(num); } })(); |
这证明了在"闭包"和"非闭包"的工作方式方面,JavaScript有多难看。
在以下情况下:
1 2 3 4 5 6 7 | 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]是一个全局函数,"console.log"(my value:"+i")是打印全局变量i
在情况下
1 2 3 4 5 6 7 8 9 | var funcs = []; function createfunc(i) { return function() { console.log("My value:" + i); }; } for (var i = 0; i < 3; i++) { funcs[i] = createfunc(i); } |
由于javascript的这种扭曲的闭包设计,"console.log"("my value:"+i);"正在从外部函数"createfunc(i)打印i。"
所有这些都是因为javascript不能设计出像C编程语言那样的函数内部的"静态"变量这样的好东西!
好啊。我通读了所有的答案。即使这里有一个很好的解释-我只是不能让这个工作。所以我上网了。https://dzone.com/articles/why-do-javascript-loop-only-use-last-value上的人有一个答案,但这里没有给出。所以我想我会贴一个简短的例子。这对我来说更有意义。
它的长短不一之处是let命令很好,但现在才被使用。然而,let命令实际上只是一个try-catch组合。这可以一直追溯到IE3(我相信)。使用尝试-捕获组合-生活是简单和好的。可能是为什么emcscript的人决定使用它。它也不需要setTimeout()函数。所以没有时间浪费。基本上,每个for循环需要一个try-catch组合。下面是一个例子:
1 2 3 4 5 6 | for( var i in myArray ){ try{ throw i } catch(ii){ // Do whatever it is you want to do with ii } } |
如果有多个for循环,只需为每个for循环放置一个try-catch组合。另外,就我个人而言,我总是使用任何变量的双字母。所以"i i"代表"i",依此类推。我在一个程序中使用这种技术将mouseover命令发送到另一个程序。
假设您不使用ES6;您可以使用iffy函数:
1 2 3 4 | var funcs = []; for (var i = 0; i < 13; i++) { funcs[i] = (function(x) { console.log("My value:" + i)})(i);} |
但会有所不同。
在
1 2 3 4 5 6 | var funcs = []; for (let i = 0; i < 3; i++) { funcs[i] = function() { console.log("My value:" + i); }; } |
为什么不在第一个(并且只有一个)循环创建后直接调用其中的每个函数,例如:
1 2 3 4 5 6 7 8 9 | 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 } |
让我们利用
1 2 3 4 5 6 7 8 9 | 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](); } |
这个问题已经有很多有效的答案了。不过,使用功能性方法的人并不多。这里有一个使用
console.log('val: '+val+'
index: '+index);
};