5 回答

TA貢獻(xiàn)1995條經(jīng)驗(yàn) 獲得超2個(gè)贊
好吧,問題是i每個(gè)匿名函數(shù)中的變量都綁定到函數(shù)外部的同一個(gè)變量。
經(jīng)典解決方案:閉包
你想要做的是將每個(gè)函數(shù)中的變量綁定到函數(shù)之外的一個(gè)單獨(dú)的,不變的值:
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中沒有塊作用域 - 只有函數(shù)作用域 - 通過將函數(shù)創(chuàng)建包裝在新函數(shù)中,可以確?!癷”的值保持不變。
2015解決方案:forEach
隨著Array.prototype.forEach函數(shù)的相對(duì)廣泛的可用性(在2015年),值得注意的是,在涉及主要在一組值上進(jìn)行迭代的那些情況下,.forEach()提供了一種干凈,自然的方式來為每次迭代獲得一個(gè)獨(dú)特的閉包。也就是說,假設(shè)您有某種包含值的數(shù)組(DOM引用,對(duì)象等等),并且設(shè)置了特定于每個(gè)元素的回調(diào)問題,您可以這樣做:
var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
// ... code code code for this one element
someAsynchronousFunction(arrayElement, function() {
arrayElement.doSomething();
});
});
這個(gè)想法是每次調(diào)用與.forEach循環(huán)一起使用的回調(diào)函數(shù)都是它自己的閉包。傳遞給該處理程序的參數(shù)是特定于該迭代的特定步驟的數(shù)組元素。如果它在異步回調(diào)中使用,它將不會(huì)與在迭代的其他步驟中建立的任何其他回調(diào)沖突。
如果你碰巧在jQuery中工作,該$.each()函數(shù)會(huì)為你提供類似的功能。
ES6解決方案: let
ECMAScript 6(ES6)引入了新的let和const關(guān)鍵字,其范圍與var基于變量的不同。例如,在具有l(wèi)et基于索引的循環(huán)中,循環(huán)中的每次迭代都將具有一個(gè)新值,i其中每個(gè)值都在循環(huán)中作用域,因此您的代碼將按預(yù)期工作。有很多資源,但我建議將2ality的區(qū)塊范圍發(fā)布作為一個(gè)很好的信息來源。
for (let i = 0; i < 3; i++) {
funcs[i] = function() {
console.log("My value: " + i);
};
}
但要注意,IE9-IE11和Edge在Edge 14支持之前l(fā)et卻出現(xiàn)了上述錯(cuò)誤(它們i每次都沒有創(chuàng)建新的,所以上面的所有函數(shù)都會(huì)像我們使用的那樣記錄3 var)。Edge 14最終做對(duì)了。

TA貢獻(xiàn)1804條經(jīng)驗(yàn) 獲得超8個(gè)贊
嘗試:
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]();}

TA貢獻(xiàn)1865條經(jīng)驗(yàn) 獲得超7個(gè)贊
使用立即調(diào)用的函數(shù)表達(dá)式,這是封裝索引變量的最簡(jiǎn)單,最易讀的方法:
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);}
這會(huì)將迭代器發(fā)送i
到我們定義為的匿名函數(shù)中index
。這將創(chuàng)建一個(gè)閉包,i
保存變量以供以后在IIFE中的任何異步功能中使用。

TA貢獻(xiàn)1824條經(jīng)驗(yàn) 獲得超5個(gè)贊
另一種說法是,i
函數(shù)中的函數(shù)在執(zhí)行函數(shù)時(shí)受到約束,而不是創(chuàng)建函數(shù)的時(shí)間。
創(chuàng)建閉包時(shí),i
是對(duì)外部作用域中定義的變量的引用,而不是創(chuàng)建閉包時(shí)的副本。它將在執(zhí)行時(shí)進(jìn)行評(píng)估。
大多數(shù)其他答案提供了通過創(chuàng)建另一個(gè)不會(huì)為您更改值的變量來解決的方法。
我想我會(huì)添加一個(gè)清晰的解釋。對(duì)于一個(gè)解決方案,就個(gè)人而言,我會(huì)選擇Harto,因?yàn)閺倪@里的答案來看,這是最不言自明的方式。發(fā)布的任何代碼都可以使用,但我選擇封閉工廠而不必寫一堆注釋來解釋為什么我要聲明一個(gè)新變量(Freddy和1800's)或者有奇怪的嵌入式閉包語法(apphacker)。
添加回答
舉報(bào)