Elaine's Blog 朝著 senior 前進的工程師

JavaScript var、let、const 差異

2019-07-02

JavaScript 作用域

  1. 全域變數

    • 全域變數在整個程式中都可以被存取與修改
    var name = "elaine";
    
    function showName() {
      console.log(name); // elaine
    }
    
    showName();
    console.log(name); // elaine
    
  2. 區域變數

    • 每次執行函式時,就會建立區域變數再予以摧毀,而且函式之外的所有程式碼都不能存取這個變數
    function showName() {
      var name = "elaine";
      console.log(name); // elaine
    }
    
    showName();
    console.log(name); // ReferenceError: name is not defined
    
    • function 由內向外找

      var name = "elaine";
      
      function showName() {
        var name = "amy";
        console.log(name); // amy
      }
      
      showName();
      console.log(name); // elaine
      

宣告提升 Hosting

  • var 有宣告提升

    console.log(a); // undefined
    var a = 123;
    

    相當於

    var a;
    console.log(a); // undefined
    a = 123;
    
  • let/const 其實也有宣告提升,但沒有初始化為 undefined,而且在賦值之前試圖取值會發生錯誤 (Temporal Dead Zone)

    • let

      console.log(a); // ReferenceError: Cannot access 'a' before initialization
      let a = 123;
      
    • const

      console.log(a); // ReferenceError: Cannot access 'a' before initialization
      const a = 123;
      

函式作用域

  • var 可用範圍以 function 為界,function 外讀不到值

    var a = 10;
    
    function hi() {
      var b = 20;
    }
    
    console.log(a); // 10
    console.log(b); // ReferenceError: b is not defined
    
    • 如果使用區塊語句像 if、else、for、while 等等區塊語句時,宣告的區域變數仍然可在整段程式碼做存取

      var a = 10;
      
      if (true) {
        var b = 20; // 全域宣告
      }
      
      console.log(a); // 10
      console.log(b); // 20
      
  • let/const 可用範圍以 block 為界,block 外讀不到值

    • let

      var a = 10;
      
      if (true) {
        let b = 20;
      }
      
      console.log(a); // 10
      console.log(b); // ReferenceError: b is not defined
      
    • const

      var a = 10;
      
      if (true) {
        const b = 20;
      }
      
      console.log(a); // 10
      console.log(b); // ReferenceError: b is not defined
      
  • 最大的差別

    • 使用 var

      setTimeout 在存取 i 的時候,都存取到相同作用域的 i (此時 i = 3)

      for (var i = 0; i < 3; i++) {
        console.log(i); // 0 1 2
        setTimeout(function() {
          console.log("這執行第" + i + ""); // 這執行第3次 這執行第3次 這執行第3次
        }, 10);
      }
      
    • 使用 let

      因為 let 會產生新的作用域,所以 setTimeout 在存取 i 的時候,是三個不同的作用域 (第一個作用域 i = 0、第二個作用域 i = 1,第三個作用域 i = 2)

      for (let i = 0; i < 3; i++) {
        console.log(i); // 0 1 2
        setTimeout(function() {
          console.log("這執行第" + i + ""); // 這執行第0次 這執行第1次 這執行第2次
        }, 10);
      }
      
    • 非得要用 var 來完成的話,只好包成一個 IIFE (立即呼叫的函式),來產生新的作用域

      for (var i = 0; i < 3; i++) {
        console.log(i); // 0 1 2
        (function(j) {
          setTimeout(function() {
            console.log("這執行第" + j + ""); // 這執行第0次 這執行第1次 這執行第2次
          }, 10);
        })(i);
      }
      

重複宣告

  • var 允許重複宣告

    var a = 123;
    var a = 456;
    console.log(a); // 456
    
  • let/const 不允許重複宣告

    let a = 123;
    let a = 456; // SyntaxError: Identifier 'a' has already been declared
    console.log(a);
    
    const a = 123;
    const a = 456; // SyntaxError: Identifier 'a' has already been declared
    console.log(a);
    

const

  • const 在宣告的時候一定要賦予值

    const a;    // SyntaxError: Missing initializer in const declaration
    
  • const 一旦被賦予值,就不可以再更改

    const a = 1;
    a = 2; // TypeError: Assignment to constant variable
    
    • 但是物件和陣列,記憶體位置不改變,值可以變動

      const a = ["第一個 value"];
      a[0] = "啦啦啦";
      console.log(a); // ["啦啦啦"]
      a = 123; // TypeError: Assignment to constant variable
      

結論

  1. 不要再用 var 來宣告變數,改用 letconst,而且優先使用 const,除非需要再指定值才用 let

    • Google 5.1.1, Airbnb 2.1/2.2
  2. 不要使用逗號,在同一行來宣告多個變數或常數,例如let a = 1, b = 2是不必要的,應該是一行一個宣告

    • Google 5.1.2, Airbnb 13.2
  3. 並不是在區塊中或函式中區域的最上面來宣告變數/常數,而是在合理的位置,在變數/常數首次被使用時的上面一行來宣告變數

    • Google 5.1.3, Airbnb 13.4

參考資料


Similar Posts

Content