1. 程式人生 > >ES6(一):let和const

ES6(一):let和const

ES6(一):let和const

一、let

1. let基本用法

  相當於var,但是又與var不同,因為let宣告的變數只能在let所在的程式碼塊中有效。
  從以下兩段程式碼以及對應的輸出結果可以很明顯的看出var與let的區別。
code:

for (var i = 0; i < 5; i++) {
    console.log(i);
}
console.log(i);

result:

0
1
2
3
4
5

code:

for (let i = 0; i < 5; i++) {
    console.log(i);
}
console.
log(i);

result:

0
1
2
3
4
ReferenceError: i is not defined

2. 塊級作用域

  眾所周知,在ES5中只有全域性作用域和函式作用域,沒有我們所謂的塊級作用域。
  這不禁讓我想起一個面試題:
code:

for (var i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

console.log(new Date, i);

  問:上面這段程式碼輸出的結果是什麼?
  仔細琢磨一下我們很容易得出正確的答案:

result:

2018-10-31T06:55:30.020Z 5
2018-10-31T06:55:31.021Z 5
2018-10-31T06:55:31.021Z 5
2018-10-31T06:55:31.022Z 5
2018-10-31T06:55:31.022Z 5
2018-10-31T06:55:31.022Z 5

  仔細看一下發現:由於setTimeout會被JavaScript延遲執行,因此是先輸出最底部的console.log,隔一秒之後再執行迴圈裡面的consol.log,此時i已經全部是5。
  那我們怎麼樣才能使輸出結果變成想要的0,1,2,3,4呢?
  明白了let的原理之後其實問題變得很簡單:
code:

for (let i = 0; i < 5; i++) {
    setTimeout(function() {
        console.log(new Date, i);
    }, 1000);
}

console.log(new Date, i);

  這樣就行了嗎?
result:

ReferenceError: i is not defined

  哈哈,剛剛還說過let只在所在的程式碼塊裡面有效,這裡最外層的console.log很明顯找不到i,所以報出引用錯誤。想要得到正確答案其實還需要去掉最後一行console.log。
  當然這個題目要擱以前的話,首先想到的解決方案應該是IIFE(Immediately Invoked Function Expression:宣告即執行的函式表示式)來解決閉包造成的問題。
code:

for (var i = 0; i < 5; i++) {
    (function(j) {  
        setTimeout(function() {
            console.log(new Date, j);
        }, 1000);
    })(i);
}

console.log(new Date, i);

result:

2018-10-31T07:17:45.825Z 5
2018-10-31T07:17:46.827Z 0
2018-10-31T07:17:46.827Z 1
2018-10-31T07:17:46.827Z 2
2018-10-31T07:17:46.827Z 3
2018-10-31T07:17:46.827Z 4

  由此我們是不是就可以得出一個結論,有了let,我們就可以拋棄IIFE了?
  這個暫時還不好說,我們還是繼續看看let的其他特性。

3. 沒有變數提升

  var命令會產生變數提升的現象,這使得js這門語言變得並不嚴謹,這一點是特點也是雞肋。用var宣告的變數,如果我們在宣告之前使用,則它的值為undefined。
  而當我們用let定義變數時,就必須嚴格按照先定義再使用的原則了,反之則會丟擲一個大大的引用錯誤(ReferenceError),顯然這更符合人們的使用習慣。
code:

console.log(a);
var a = 2;

console.log(b);
let b = 2;

result:

undefined
ReferenceError: b is not defined

4. 暫時性死區及不能重複宣告

  ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。
  總之,在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。
  “暫時性死區”也意味著typeof不再是一個百分之百安全的操作。
  let不允許在相同作用域內,重複宣告同一個變數。

二、const

1. const基本用法

  const命令是宣告一個常量,用法和let一樣。

  • 和let的相同之處是:
    • const與let作用域相同,都是隻在宣告的程式碼塊中起作用
    • const也不會提升所宣告的常量
    • const也不能重複宣告
  • 不同之處是const宣告的常量在宣告時就必須賦值,因為一旦宣告就不能改變改常量的值

2. 真的不能改變嗎?

  const實際上保證的,並不是變數的值不得改動,而是變數指向的那個記憶體地址所儲存的資料不得改動。對於簡單型別的資料(數值、字串、布林值),值就儲存在變數指向的那個記憶體地址,因此等同於常量。但對於複合型別的資料(主要是物件和陣列),變數指向的記憶體地址,儲存的只是一個指向實際資料的指標,const只能保證這個指標是固定的(即總是指向另一個固定的地址),至於它指向的資料結構是不是可變的,就完全不能控制了。因此,將一個物件宣告為常量必須非常小心。
code&result:

const a=1;
a=2;//TypeError
const obj={
    b:3,
    c:4
}
obj.b=5;
console.log(obj);//{b:5,c:4}

參考資料