var與ES6中const、let宣告的變數的區別

YinghaoGuo發表於2017-06-02

好久以前的研究,今天再來回顧一下。

首先我們比較一下使用var宣告的變數和不使用var宣告的變數的區別:

  1. 使用var宣告的變數宣告前呼叫,那麼該變數的值為undefined;不使用var宣告的變數宣告前呼叫直接報錯Uncaught ReferenceError:xxx is not difined;
  2. 使用var宣告的變數不可delete,不使用var宣告則可以delete掉釋放空間。(實則瀏覽器的垃圾回收機制也會清理掉用var宣告但是不再使用的的變數,比如某個僅執行一次的函式中被呼叫的某個var 宣告的變數)
  3. 未使用var宣告的變數實際上是window的一個物件,而使用var宣告的變數只是一個本地變數而已。
  4. 在使用'use strict'的模式之下,不使用var 宣告變數的語法是不被允許的,報錯:Uncaugth SyntaxError;
    console.log(a);   //undefined,這裡還涉及變數提升的概念
    console.log(b);   //報錯 Uncaught ReferenceError: b is not defined
    var a = 1;
    b = 2;
    console.log(a);   //1
    console.log(b);   //2
    console.log(window.a);   //undefined
    console.log(window.b);   //2
    delete a;
    delete b;
    console.log(a);   //1
    console.log(b);   //報錯 Uncaught ReferenceError: b is not defined,可見b在宣告前使用和宣告後delete再使用是一樣的結果
再來看下let,var,const宣告的變數的區別:
  1. 看意思就知道,const是常量的意思,就是說只能被定義一次,且不可再改變,否則就會報錯:Uncaught TypeError: Assignment to constant variable.另外宣告前使用也會報錯:Uncaught ReferenceError: a is not defined;看一個例子:
    const foo = {};
    foo.prop = 123;
    console.log(foo.prop);  //123
    foo.prop = 456;
    console.log(foo.prop);  //456
    const foo = {};   //Identifier 'foo' has already been declared
    這裡const定義一個foo之後,對其新增了屬性prop,隨後又對該屬性進行了修改,為什麼沒有報錯?那是因為常量foo本身儲存的是一個地址,該地址指向一個物件,不可變的是foo這個地址,即不能把foo指向另一個地址,但物件本身是可變的,所以依然可以為foo新增新屬性,改變新屬性值,而最後一行中嘗試改變foo的地址,就會報錯,如果要徹底將物件凍結,應該使用Object.freeze(obj)方法。同理,陣列也是一樣的
    const group = [];
    group.push('Bob');
    console.log(group[0]); //Bob
    group = ["Tom"];//Uncaught TypeError: Assignment to constant variable.
  2. var 和 let 就有意思了,let宣告的變數宣告前使用也會報錯,這個與const一致;最重要的一點是let宣告瞭一個塊級作用域的變數在一個塊的“}”結束的時候,該變數消失。例子:
    (function(x,y){
            var b = x;
            let c = y;
            if (true) {
                var b = 5;
                let c = 6;
                console.log(b);  //5
                console.log(c);  //6,這裡的let c在下一行的"}"之後消失
            }
            console.log(b);  //5
            console.log(c);  //3 ,這裡仍然是第三行的let c;
    }(2,3));
  3. 再看一個例子:
    var array1 = [],array2 = [];
    for(var  i=0;i<10;i++){ 
            array1[i] = function(){
                console.log(i);
            };
    }
    for(let  j=0;j<10;j++){  
            array2[j] = function(){
                console.log(j);
            };
    }
    array1[6]();  //10
    array2[6]();  //6
    所以說let是一個比較保守的變數。這裡變數為i的for迴圈中,i是一個全域性變數,array1[i]是一個console.log(i)的函式,而i的最終結果為10,故而每次呼叫array1[i],實際上都是console.log(10);而let作為僅在其程式碼塊有效的變數,當前的j僅在本輪的迴圈中有效,就是說每一次迴圈,j其實都是一個新產生的變數。所以let變數適合用於迴圈體中
  4. 再看一個例子:
    var tmp = 1;
    if(true){
        tmp = 2;   //ReferenceError
        let tmp;
    }
    這個例子中tmp=2的賦值會報錯,因為if中的let對tmp變數的宣告,導致該tmp繫結了這個作用域,而let不會像var那樣“變數提升”,所以未宣告賦值會報錯。ES6中明確規定:如果區塊中存在let和const命令,則這個區塊對這些命令宣告的變數從一開始就形成了封閉做作用域,只要在宣告之前做任何使用,都會報錯。這在語法上稱為“暫時性死區”(temporal dead zone ,簡稱TDZ)。
  5. 還有一點要注意,var宣告的變數為全域性變數,而let,const宣告的變數不為全域性變數,使用window訪問不到,如下:
    var a = 1;
    console.log(window.a); //1
    let b = 2;
    console.log(window.b) //undefined


相關文章