[英]how to interpret the result ( understand let and var in js)
考慮以下代碼
var fs = []; for(var i=0;i<10;i++){ fs.push(i => console.log(i)); } fs.forEach( f => f());
如果功能更改為:
for(let i=0;i<10;i++){
fs.push(function(){
console.log(i)
});
}
它將打印1,2,3,4,5,6,7,8,9預期輸出。
我不明白為什么。 有人可以幫忙嗎?
let和var 對於為什么 undefined
10倍的代碼 undefined
。
確實有所不同的是,您的箭頭函數是使用參數i
定義的(覆蓋外部索引i
),而在第二個示例中則不是。
const fs = []; for (var i = 0; i < 10; i++) { fs.push(() => console.log(i)); } fs.forEach(f => f());
如果將該參數添加到第二個示例中,則還會得到10x undefined
。
const fs = []; for (let i = 0; i < 10; i++) { fs.push(function(i) { console.log(i) }); } fs.forEach(f => f());
var
和let
在代碼中的區別更加細微:
const fs = []; for (var i = 0; i < 10; i++) { fs.push(() => console.log(i)); } fs.forEach(f => f());
const fs = []; for (let i = 0; i < 10; i++) { fs.push(() => console.log(i)); } fs.forEach(f => f());
使用let
時,數組中的每個函數都有一個局部范圍內的閉包i
,而使用var
在執行時, i
為10
(因為每個函數中只有一個變量i
被閉包)。
有2個不同的問題需要詳細介紹。 讓我們集中討論第一種情況,主要問題是定義ES6箭頭函數,然后在不使用參數的情況下調用它:
當轉換為ES5匿名函數表示法時, i => console.log(i)
為:
function(i){ console.log(i) }
因此,在i => console.log(i)
,您可以看到一個接受參數i
的匿名函數的簡寫ES6定義(也稱為ES6箭頭函數符號 )。
最終結果是console.log(i)
嘗試打印undefined
的i
,因為在執行時未將其傳遞給該arrow函數 。
您重新推送了一個函數定義,稍后您將在不 向其傳遞參數的情況下執行該定義,該參數實際上需要在控制台中將其輸出。
var fs = []; for(var i=0; i<10; i++){ // You are pushing to the array the below anonymous function definition fs.push( // You are creating an anonymous function which accepts i and console.logs it i => console.log(i) ); } fs.forEach( // You are calling the "pushed" above function definition with NO parameter i f => f() );
現在讓我們探討第二個代碼示例的原因和方式,以及var/let
如何在控制台輸出中發揮重要作用:
let fs = [] // You define i in the for-loop block score for(var i=0; i<10; i++){ fs.push( // You push an annonymous function definition wich has a reference of i in the same block scope // However i is already here 10 since the loop is already done and it takes the last value of i function(){ console.log(i) } ); } fs.forEach( // You call f which has a reference inside to the i from the for loop f => f() );
因此,在這種情況下, i
當你使用var i
的i
,因為它不保留其詞法塊范圍最終被更新為10
的的console.log被調用之前。
現在let
用let
嘗試let
:
let fs = [] // You define i via LET in the for-loop block score for(let i=0; i<10; i++){ fs.push( // You push an annonymous function definition wich has a reference of i in the same block scope // i retains its lexical scope in for loops function(){ console.log(i) } ); } fs.forEach( // You call f which has a reference inside to the i from the for loop f => f() );
讓讓更多:
let允許您聲明范圍限於使用 塊 , 語句或表達式的變量。 這與 var關鍵字 不同 ,var關鍵字 在全局范圍內或整個函數本地定義變量,而不管塊范圍如何。
在第一示例中, i
在fs.push(i => console.log(i));
是箭頭功能的參數。 這實際上等效於fs.push(function(i){ return console.log(i); });
此參數與循環的迭代器具有相同的名稱。 在要推送的功能范圍內,參數i
優先於外部變量。 如果要捕獲迭代器,則不應將其命名為與參數相同的名稱,如下所示:
fs.push(() => console.log(i));
現在您將看到在這種情況下var
和let
之間存在區別。 您的輸出將是10,10,10,10,10,10,10,10,10,10,10。 這是因為var
和let
的作用域不同。 當使用var
聲明時,循環迭代器在整個循環過程中將是同一對象。 所有功能都捕獲相同的對象。 循環結束后( i++
發生,然后進行條件檢查)的最終值為10,因此它們全部顯示為10。使用let
,迭代器在循環的每次迭代中都是一個新對象,因此每個函數都捕獲一個新對象,該對象持有當前循環數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.