2017-07-12 65 views
3

下面是代碼,我覺得閉合功能有一個怪異的行爲一個小片段...JavaScript的關閉不正確的行爲

var arr = [5, 6, 7, 8, 9, 0]; 
 
var someFn; 
 

 
arr.forEach(function(val, idx) { 
 
    if (!someFn) { 
 
    someFn = function() { 
 
     console.log(`B: ${idx} : ${val}`); 
 
    }; 
 
    } 
 
    console.log(`A: ${idx} : ${val}`); 
 
    someFn(); 
 
});

最終控制檯輸出...

A: 0 : 5 
B: 0 : 5 
A: 1 : 6 
B: 0 : 5 
A: 2 : 7 
B: 0 : 5 
A: 3 : 8 
B: 0 : 5 
A: 4 : 9 
B: 0 : 5 
A: 5 : 0 
B: 0 : 5 

我希望someFn當在foreach是處理,但它總是輸出第一個值是"idx: 0, val: 5"處理的遞增值。

我不認爲這是正確的行爲,因爲someFn正在創建一個閉包,它包含變量idxval,並且這兩個變量都在外層函數中發生變化。

感激,如果有人可以慈祥解釋這種行爲。

+0

我想這是因爲你宣佈'someFn( )'帶有'idx'和'val'的當前值,一旦你繼續調用它,你就不會傳遞它們的新值。 – Lixus

+0

如果你的條件搞砸了,請使用: var arr = [5,6,7,8,9,0]; var someFn; arr.forEach(功能(VAL,IDX){ someFn =函數(){ 的console.log('B:$ {IDX}:$ {VAL}'); }; someFn(); }); 它會工作。 –

+1

@baao該問題與** template literals **無關。所以我投票重新打開這個。 –

回答

0

根據這一other answer上SO:

封閉是當一個函數就開始執行它被分配一個堆棧幀...

所以每個函數調用創建自己的封閉。

什麼forEach確實是它需要一個函數(回調)並多次調用它(從數組中傳遞元素以及它的索引和數組)。所以,每個迭代forEach創建一個新的閉包。

您在第一次迭代中定義了someFn(之後從未重新聲明),因此它所關閉的閉包是第一次迭代的結束。因此唯一可用的值是第一次迭代中的值。

封閉與功能本身無關,它與其調用有關。

實施例:

function theFunction(value) { 
 
    return function() { 
 
    return value; 
 
    }; 
 
} 
 

 
var someFn1 = theFunction("Ibrahim"); 
 
var someFn2 = theFunction("John"); 
 

 
console.log("someFn1: ", someFn1()); 
 
console.log("someFn2: ", someFn2());

在該示例中,每個調用theFunction創建一個新的閉合。 someFn1someFn2,儘管它們是由同一個函數生成的,但沒有獲得相同的關閉。

在您的代碼中相當於theFunction是傳遞給forEach的匿名函數,它被執行(因此創建clousures)的次數與數組中的元素的次數相同。

+0

我明白這一點。但我相信你的回答是正確的。雖然我認爲我理解封鎖的確很好,但很容易陷入這樣的陷阱。謝謝,我投了你的票。 – Praj

+0

不客氣!是關閉可能會很棘手。 –

0

我希望someFn正在創造一個封閉包圍變量idxval和這兩個變量在外部功能正在發生變化。

不,他們不會改變。它們是每次調用外部函數時實例化的新變量。來自第一次迭代的兩個變量,在那裏創建的閉包關閉了,保留了它們的值。

要獲得預期的行爲,你可以使用一個循環沒有的功能,在這裏你只聲明瞭兩個變量:

var arr = [5, 6, 7, 8, 9, 0]; 

for (var [idx, val] of arr.entries()) { 
// ^^^^^^^^^^^^^^ global variables 
    if (!someFn) { 
    var someFn = function() { 
     console.log(`B: ${idx} : ${val}`); 
    }; 
    } 
    console.log(`A: ${idx} : ${val}`); 
    someFn(); 
} 

雖然usually that's exactly what we try to prevent :-)