1,塊級作用域。這里想說的是,在一個塊{}中,使用const或let 聲明一個變量,這個變量將統治整個塊(consumes the entire scope),無論你在塊的什么地方聲明這個變量。就算,你是在塊的底部聲明了一個變量, 但是這個變量的作用域,實際上從塊的起始部位開始算起,一直到塊的結束,存在着某種意義上的變量提升。
if(true) { // data 變量的作用域從這里開始 console.log(1); console.log(2) let data = new Date(); // data 變量的作用域在這里開始 }
變量存在提升,這也就意味着,我們有可能無意地使用了后面聲明的變量,尤其是當變量作用域外部有一個和它重名的變量時
let data = 1; if(true) { console.log(data); console.log(1); console.log(2) let data = new Date(); }
運行這段代碼,報錯了。剛開始的時候,以為第一行的data 讀取的是外部的變量data(1), 因為內部的data 變量還沒有聲明。其實不是,只要在塊中聲明了變量,這個變量將統治整個塊。第一行的console.log 讀取的是塊中最后一行聲明的變量。盡管能讀取變量,但不能使用這個變量。在一個作用域中,在let 或const 變量聲明之前,使用這個變量,將拋出引用錯誤。能夠讀取,但不能使用變量的區域就是暫存死區。在一個作用域中聲明一個變量,暫存死區,指的就是作用域開始位置,到變量聲明位置結束,引用暫存死區中的變量就會拋出引用錯誤。
2,const 指的是不能重新賦值, 而不是值不能改變。Constants can not be reassigned. Constants are not immutable.
一定要區分值和變量。5,false, {name: 'sam'} 就是值。變量,是需要聲明的,let a; 它其實是在內存中開辟了一個空間,用於保存東西。變量和值是相互獨立的。可以聲明一個變量,不給它值,那這個變量就什么都不能做。也可以只寫一個值,不賦值給任何變量,但這個值只能用一次。當把一個值賦給一個變量的時候,它們有了關系,產生了綁定,變量名和值綁定到一起了,這叫assigin. const 指的就是這一塊內容。值能不能改變,只關系值本身,它不關系和變量的關系。數字5,它就不能再改變了, 但對象就可以改變。
3, let 和 const 在for 循環中使用
let 聲明的變量只在for 的循環體中有效,循環結束后 變量就消失了, 同時const 也可以在for 循環中聲明變量,但是不能用於 常規的for 循環中。所謂的常規for 循環就是for(let i =0; i < 10; i++) 的格式。
在使用for 循環的時候,每一次的迭代都會重新聲明一個變量。像for(let i = 0; i < 10; i++); 這樣使用時,i 變量聲明了10次,只不過每一次迭代給i 賦值不一樣而已,並且變量只在循環體中使用。 我們可以這樣理解: 第一次迭代的使用,聲明了一個變量i, 賦值為0, 0 < 10, 然后執行循環體,執行完之后i++ 變成了1. 這一次迭代就結束了,這個i 的使命就完成了。然后進行第二次迭代,這時重新聲明一個變量i, 不過這次給他賦值為1,1 < 10 繼續執行循環體,然后加1. 這次迭代又結束了,這個i 也完成了使命,消失了。第三次迭代進行同樣的操作,聲明一個全新的變量i,執行循環體之類的,直達整個循環結束。
對於for 循環來說,每一次的迭代都是重新聲明一個全新的變量i,只是賦的值是上一次迭代完成時的值,這樣的話,循環體內獲取到的i, 每次也都是全新的變量i,而不是像使用 var 聲明時得到的是全局變量,並且,每一次迭代完成后,i 變量就消失了。
let funs = []; // 使用var 聲明循環變量i for (var i = 0; i < 10; i++) { funs.push(function(){ console.log(i); }) } // funs.forEach(item => item()); // 輸出10個10 // 改為使用let 聲明循環變量 let funss = []; for (let i = 0; i < 10; i++) { funss.push(function(){ console.log(i); }) } funss.forEach(item => item()); // 輸出0, 1, 2, 3, 4, 5, 6, 7, 8, 9
除了常規的for 循環之外,還有for-in 和for-of 操作, 原理都是一樣的,他們每一次的迭代都是重新聲明一個全新的迭代對象,而不是給原來聲明的迭代對象賦新值, 循環體內獲取到的都是當前迭代對象的值。
let funcs = []; let arr = [1, 2, 3]; // for-in 循環, 數組是不建議使用for-in ,這里只是簡單的演示 for (let key in arr) { funcs.push(function() { console.log(key); }); } funcs.forEach(function(func) { func(); // 輸出0, 1, 2 }); // 使用for-of funcs = []; for (let key of arr) { funcs.push(function() { console.log(key); }); } funcs.forEach(function(func) { func(); // 輸出1, 2,3 });
現在看一下const, const 也可以使用在for循環中。最簡單的就是把上面的三個for 循環中的let 都轉換為const. for (const i = 0; i < 10; i++) {}; for (const key of arr) {} , for (const key in arr) {} . 這時你會發現第一種常規for 循環報錯了。看一下第一次迭代就知道了。聲明了一個 變量i, 賦值為0。 但這里使用const, 也就意味着i 在聲明之后,就不能再改變了。好了,0 < 10, 執行循環體,然后 加1,報錯了,i 不能變化了。一次迭代都沒有走完,就報錯了,說明,在使用常規for 循環時, const 不能用來聲明變量。
再來看一下,for-of, for-in, 沒有問題,因為每一次的迭代都會聲明一個全新的key, 所有的賦值都是給一個新的變量賦值,而沒有改變原來的值。那使用let 和 const 有什么區別嗎? 當然有了,還是在於const 聲明的變量不能重新賦值了,所以如果for-in 或for- of 中使用const 聲明了變量( 如key), 循環體中,就不能給key 賦新值了,如果使用let ,那就無所謂了,想干什么就干什么。只不過for-in 或for-of 中,我們很少改變key 值,所以他們在實際使用時就沒有什么區別了。