1. 程式人生 > >let 、 const 、 塊級作用域

let 、 const 、 塊級作用域

本文為學習筆記;

原文連線

寫在前面

ES6 宣告變數的 6 種方法

ES5 只有兩種宣告變數的方法:var 命令和 function 命令。ES6 除了新增 letconst 命令,後面章節還會提到,另外兩種宣告變數的方法:import 命令和 class命令。所以,ES6 一共有 6 種宣告變數的方法。

塊級作用域

為什麼需要塊級作用域

ES5 只有全域性作用域和函式作用域,沒有塊級作用域,這帶來很多不合理的場景。比如內層變數可能會覆蓋外層變數;用來計數的迴圈變數洩露為全域性變數

  • let 實際上為 JavaScript 新增了塊級作用域。
    塊級作用域的出現,實際上使得獲得廣泛應用的立即執行函式表示式(IIFE)不再必要了。

塊級作用域與函式宣告

  • 在瀏覽器的 ES6 環境中,塊級作用域內宣告的函式,行為類似於 var 宣告的變數;
  • 另外,還有一個需要注意的地方。ES6 的塊級作用域允許宣告函式的規則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。

let

不存在變數提升

  • let 所宣告的變數只在其所在的程式碼塊內有效
  • let 不存在變數提
    var 所宣告的變數在指令碼執行前就將變數都定義了,在指令碼中的賦值語句中再對其進行賦值操作,所以變數可以在宣告之前使用,值為undefined
    let 糾正了這種語法行為,它所宣告的變數一定要在變數聲明後使用,否則報錯

暫時性死區(temporal dead zone
,簡稱 TDZ)

  • ES6 中如果在區塊中存在 letconst ,則區塊對這些命令宣告的變數從一開始就形成了封閉作用域,凡是在宣告之前使用這些變數都會報錯
  • ES6 規定暫時性死區和 letconst 語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。
  • 在沒有let之前,typeof運算子是百分之百安全的,永遠不會報錯。現在這一點不成立了。這樣的設計是為了讓大家養成良好的程式設計習慣,變數一定要在宣告之後使用,否則就報錯。

    ES6 規定暫時性死區和let、const語句不出現變數提升,主要是為了減少執行時錯誤,防止在變數宣告前就使用這個變數,從而導致意料之外的行為。這樣的錯誤在 ES5 是很常見的,現在有了這種規定,避免此類錯誤就很容易了。
    總之,暫時性死區的本質就是,只要一進入當前作用域,所要使用的變數就已經存在了,但是不可獲取,只有等到宣告變數的那一行程式碼出現,才可以獲取和使用該變數。

不允許重複宣告

let 不允許在相同作用域內,重複宣告同一個變數。

const

  • const 宣告一個只讀的常量。一旦宣告,常量的值就不能改變;(這意味著,const 一旦宣告變數,就必須立即初始化,不能留到以後賦值)
  • const 的作用域與let命令相同:只在宣告所在的塊級作用域內有效。
  • const 命令宣告的常量也是不提升,同樣存在暫時性死區,只能在宣告的位置後面使用。

const 的本質

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

  • 如果真的想將物件凍結,應該使用Object.freeze方法。


    const foo = Object.freeze({});
    // 常規模式時,下面一行不起作用;
    // 嚴格模式時,該行會報錯
    foo.prop = 123;

    上面程式碼中,常量foo指向一個凍結的物件,所以新增新屬性不起作用,嚴格模式時還會報錯。
    除了將物件本身凍結,物件的屬性也應該凍結。下面是一個將物件徹底凍結的函式。

    var constantize = (obj) => {
    Object.freeze(obj);
    Object.keys(obj).forEach( (key, i) => {
    if ( typeof obj[key] === 'object' ) {
    constantize( obj[key] );
    }
    });
    };