亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

循環內的JavaScript閉包 - 簡單實用的例子

循環內的JavaScript閉包 - 簡單實用的例子

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}它輸出這個:我的價值:3 我的價值:3 我的價值:3而我希望它輸出:我的價值:0 我的價值:1 我的價值:2使用事件偵聽器導致運行函數的延遲時,會出現同樣的問題: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.  });}<button>0</button><br><button>1</button><br><button>2</button>...或異步代碼,例如使用Promises:// Some async wait functionconst 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.}這個基本問題的解決方案是什么?
查看完整描述

5 回答

?
拉風的咖菲貓

TA貢獻1995條經驗 獲得超2個贊

好吧,問題是i每個匿名函數中的變量都綁定到函數外部的同一個變量。


經典解決方案:閉包

你想要做的是將每個函數中的變量綁定到函數之外的一個單獨的,不變的值:


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中沒有塊作用域 - 只有函數作用域 - 通過將函數創建包裝在新函數中,可以確?!癷”的值保持不變。


2015解決方案:forEach

隨著Array.prototype.forEach函數的相對廣泛的可用性(在2015年),值得注意的是,在涉及主要在一組值上進行迭代的那些情況下,.forEach()提供了一種干凈,自然的方式來為每次迭代獲得一個獨特的閉包。也就是說,假設您有某種包含值的數組(DOM引用,對象等等),并且設置了特定于每個元素的回調問題,您可以這樣做:


var someArray = [ /* whatever */ ];

// ...

someArray.forEach(function(arrayElement) {

  // ... code code code for this one element

  someAsynchronousFunction(arrayElement, function() {

    arrayElement.doSomething();

  });

});

這個想法是每次調用與.forEach循環一起使用的回調函數都是它自己的閉包。傳遞給該處理程序的參數是特定于該迭代的特定步驟的數組元素。如果它在異步回調中使用,它將不會與在迭代的其他步驟中建立的任何其他回調沖突。


如果你碰巧在jQuery中工作,該$.each()函數會為你提供類似的功能。


ES6解決方案: let

ECMAScript 6(ES6)引入了新的let和const關鍵字,其范圍與var基于變量的不同。例如,在具有let基于索引的循環中,循環中的每次迭代都將具有一個新值,i其中每個值都在循環中作用域,因此您的代碼將按預期工作。有很多資源,但我建議將2ality的區塊范圍發布作為一個很好的信息來源。


for (let i = 0; i < 3; i++) {

    funcs[i] = function() {

        console.log("My value: " + i);

    };

}

但要注意,IE9-IE11和Edge在Edge 14支持之前let卻出現了上述錯誤(它們i每次都沒有創建新的,所以上面的所有函數都會像我們使用的那樣記錄3 var)。Edge 14最終做對了。


查看完整回答
反對 回復 2019-05-20
?
胡說叔叔

TA貢獻1804條經驗 獲得超8個贊

嘗試:

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


查看完整回答
反對 回復 2019-05-20
?
莫回無

TA貢獻1865條經驗 獲得超7個贊

使用立即調用的函數表達式,這是封裝索引變量的最簡單,最易讀的方法:

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

這會將迭代器發送i到我們定義為的匿名函數中index。這將創建一個閉包,i保存變量以供以后在IIFE中的任何異步功能中使用。


查看完整回答
反對 回復 2019-05-20
?
滄海一幻覺

TA貢獻1824條經驗 獲得超5個贊

另一種說法是,i函數中的函數在執行函數時受到約束,而不是創建函數的時間。

創建閉包時,i是對外部作用域中定義的變量的引用,而不是創建閉包時的副本。它將在執行時進行評估。

大多數其他答案提供了通過創建另一個不會為您更改值的變量來解決的方法。

我想我會添加一個清晰的解釋。對于一個解決方案,就個人而言,我會選擇Harto,因為從這里的答案來看,這是最不言自明的方式。發布的任何代碼都可以使用,但我選擇封閉工廠而不必寫一堆注釋來解釋為什么我要聲明一個新變量(Freddy和1800's)或者有奇怪的嵌入式閉包語法(apphacker)。


查看完整回答
反對 回復 2019-05-20
  • 5 回答
  • 0 關注
  • 1180 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號