var、let、const變數宣告的區別及特點

crayons發表於2019-12-13

var、let、const這些變數宣告各有什麼區別及特點,本文將從三個方面闡述他們差別:

  • 變數提升機制
  • 塊級作用域繫結
  • 塊級繫結的最佳實踐

一、var變數提升機制及特點

在函式作用域或全域性作用域中通過var宣告的變數,都會被當成在當前作用域頂部宣告的變數,這就是變數提升機制,下面用一個函式例子說明:

    function getAge(name){
        if(name){
            var age = '28';
            return age;
        }
        // 這裡也能訪問到if語句裡的age變數,不過因為變數未初始化,結果為undefined
        console.log(age);   
    }
複製程式碼

其實js在預編譯階段,會把程式碼修改成下面這樣子:

    function getAge(name){
        var age;
        if(name){
            age = '28';
            return age;
        }
        console.log(age);  // 結果也為undefined
    }
複製程式碼

從上面的示例中可以看到,變數age的宣告被提升至函式頂部,而初始化操作依舊在if語句中完成。 不過還有一點要注意的是,除了var有提升機制,函式也有,請看下面示例:

    console.log(age) // ƒ age() {console.log(123);}
    age(); // 28
    var age = '28'
    function age(){
        console.log('28');
    }
    age() // Uncaught TypeError: age is not a function
複製程式碼

上述示例說明:函式提升與變數提升同時存在時,函式提升優先順序高於變數提升並且,並且不會被變數宣告覆蓋,但是當變數賦值之後age函式被覆蓋。

注:還有一個要注意的點,當var被用於全域性作用域時,它會建立一個全域性物件(瀏覽器環境中的window物件)的屬性,所以使用var在全域性作用域宣告的時候要特別注意,全域性物件中是否存在這屬性,防止被覆蓋

    var age = '40';
    console.log(age);  // 40
    console.log(window.age) // 40
    
    var RegExp = "hello,world";
    // 本來window全域性物件中有js內建的RegExp函式,結果被var宣告的RegExp變數給覆蓋了。
    console.log(window.RegExp);  // hello,world
複製程式碼

二、塊級作用域繫結

1、let宣告

在上面我們提到var宣告會被提升至作用域頂部,由此可能會引發一些奇怪的BUG,正因為如此, ES6 引入了塊級宣告(let、const),塊級宣告也就是讓所宣告的變數在指定塊級作用域外無法被訪問,讓變數的生命週期更加可控。還是以上面的例子說明:

    function getAge(name){
        if(name){
            let age = '28';
            return age;
        }
        // 如果使用let塊級宣告,這裡會報錯:
        // Uncaught ReferenceError: age is not defined
        console.log(age);   
    }
複製程式碼

上述例子說明age變數只有在if程式碼塊裡可以訪問,其它地方無法訪問該變數,這說明let會在一個函式內部或在一個程式碼塊(由一對花括號包裹)內部會建立一個塊級作用域。 下面這個例子也很好的說明了這個問題:

   for(var i = 0;i<10;i++){}
   console.log(i); // 10
複製程式碼

因為var宣告的變數被提升到全域性作用域頂部,迴圈體外也可以訪問,也導致了變數i在迴圈的每次迭代中都被共享了,所以得到的結果是:10, 但使用let就可以解決這個問題:

   for(let i = 0;i<10;i++){}
   console.log(i); // Uncaught ReferenceError: i is not defined
複製程式碼

使用let宣告的話,i變數只在迴圈體內可以訪問,其它地方都訪問不到。 同時let也禁止重複宣告:

    var age="40";
    var age = "50";
    // 如果var宣告,後面的宣告會覆蓋前面的宣告,所以這裡列印結果結果是:50
    console.log(age); 
    
    let name = "張三";
    let name = "李四";
    // 使用let重複宣告,會出現報錯:
    // Uncaught SyntaxError: Identifier 'name' has already been declared
    console.log(name);
複製程式碼

let和var宣告還有一點不同的是,在全域性作用域中let宣告不會像var一樣會在全域性window物件中建立一個屬性。

    var age = '40';
    console.log(window.age === age); // true
    let name = '張三';
    console.log(window.name === name); // false
複製程式碼

且let並不會像var宣告被提升到作用域頂部

    console.log(name);  // undefined
    var name = '張三';
    console.log(age); // 報錯:Uncaught TypeError: age is not a function。
    let age = '40';
複製程式碼

因為var宣告會被提升至作用域頂部,所以此時訪問name是存在的,但是name此時並未被賦值,所以得到undefined,但是如果使用let或const來宣告變數,因為它們並不具備提升機制,所以在宣告前訪問會報錯,也稱暫時性死區。

2、const宣告

const的作用很多時候和let是一樣的,但通過const宣告的變數為基礎資料型別時,它是不可變的,當它宣告的變數為引用型別時,可以修改它的屬性,如果要確保引用型別值的屬性值不可變,請使用Object.freeze()凍結

   let age = '40';
   age = '50';
   console.log(age); // 50
   const name = '張三';
   name = '李四'; 
   console.log(name); // 報錯:Uncaught TypeError: Assignment to constant variable.
   const person = {
       name:'張三'
   }
   person.name = '李四';
   console.log(person.name); // 李四
複製程式碼

三、塊級繫結的最佳實踐

在ES6中一般情況下需要使用變數宣告的地方都應該使用let或const替代var,在預設情況下建議使用 const,因為一般變數初始化之後都不應該被改變,有助於防止某些型別的錯誤,但變數值確實需要被更改的情況下才使用let。

相關文章