ES6中 let 和 const 命令熟悉

ES6

1.0 let 和 const 命令

1.0 let 命令

ES6新增了 let命令, 但是它與 var不同的是, let所聲明的變量, 只在 let命令所在的代碼塊內有效.

{
    let a = '我是let聲明變量';
    var b = '我是var聲明變量'
}
// console.log(a); // ReferenceError: a is not defined
console.log(b);

let 聲明的變量不存在變量提升

存在暫時性死區

只要塊級作用域內 存在 let命令, 它所聲明的變量就"綁定(binding)" 這個區域, 不再受外部影響

var temp = 123
if (true) {
    temp = 'abc'; // ReferenceError: temp is not defined
    let temp
    console.log(temp);
}

上面這段代碼中, 存在全局變量temp, 但是塊級作用域內 let 又聲明瞭一個局部變量 temp , 導致塊級作用域內的局部變量temp綁定這個塊級作用域, 全局變量temp對塊級作用域無效, 所以在 let 聲明變量之前, 對 temp賦值會報錯.

ES6明確規定, 如果區塊中 存在 letconst 命令 這個區塊對用 letconst 聲明的變量, 從一開始就形成了封閉作用域. 凡是在聲明變量之前就使用這些變量, 就會報錯.

總之, 在代碼塊內, 使用 let 命令聲明變量之前, 該變量都是不可用的. 這在語法上, 成爲 “暫時性死區” (temporal dead zone, 簡稱 TDZ)

if (true) {
  // TDZ開始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ結束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

上述代碼中, 在 let 命令聲明變量 tmp 之前, 都屬於tmp 的 “死區”

2.0 塊級作用域

爲什麼需要塊級作用域?

ES5 只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景

  • 場景一: 內層變量可能會覆蓋外層變量

    var tmp = new Date();
    
    function f() {
      console.log(tmp);
      if (false) {
        var tmp = 'hello world';
      }
    }
    
    f(); // undefined
    

    上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,內部使用內層的tmp變量。但是,函數f執行後,輸出結果爲undefined,原因在於變量提升,導致內層的tmp變量覆蓋了外層的tmp變量。

  • 場景二: 用來計數的循環變量泄露爲全局變量

    var s = 'hello';
    for (var i = 0; i < s.length; i++) {
        console.log(s[i]);
    }
    console.log(i); // 5
    

    上面代碼中, 變量 i 只是用來控制循環, 但循環結束後, 它並沒有消失, 泄露成了全局變量. 這種方式不安全.

3.0 ES6 的塊級作用域

let 實際上爲 JavaScript新增了塊級作用域

function f1() {
    let n = 5;
    if (true) {
        let n = 10; // 塊級作用域內, 不影響外部的同名變量 n
    }
    console.log(n); // 5
}
f1()

4.0 const 命令

const 聲明一個只讀的常量, 一旦聲明, 常量的值不能改變.

const聲明的變量不得改變值,這意味着,const一旦聲明變量,就必須立即初始化,不能留到以後賦值。

const foo;
// SyntaxError: Missing initializer in const declaration
  • const的作用域與let命令相同:只在聲明所在的塊級作用域內有效
  • const命令聲明的常量也是不提升,同樣存在暫時性死區,只能在聲明的位置後面使用。

本質:

const實際上保證的,並不是變量的值不得改動,而是變量指向的那個內存地址所保存的數據不得改動。對於簡單類型的數據(數值、字符串、布爾值),值就保存在變量指向的那個內存地址,因此等同於常量。

但是: 對於複合類型的數據(主要是對象和數組),變量指向的內存地址,保存的只是一個指向實際數據的指針,const只能保證這個指針是固定的(即總是指向另一個固定的地址),至於它指向的數據結構是不是可變的,就完全不能控制了。因此,將一個對象聲明爲常量必須非常小心。

const foo ={}
foo.name = 'Gene' // 爲foo對象添加一個屬性, 可以添加成功
console.log(foo); // { name: 'Gene' }
foo = {} // 將foo指向另一個值或對象就會報錯

上面代碼中,常量foo儲存的是一個地址,這個地址指向一個對象。不可變的只是這個地址,即不能把foo指向另一個地址,但對象本身是可變的,所以依然可以爲其添加新屬性。

再看如下的例子

const a = [];
a.push('Hello'); // 可執行
a.length = 0;    // 可執行
a = ['Dave'];    // 報錯

上面代碼中,常量a是一個數組,這個數組本身是可寫的,但是如果將另一個數組賦值給a,就會報錯。

5.0 頂層對象的屬性

頂層對象,在瀏覽器環境指的是window對象,在 Node 指的是global對象。ES5 之中,頂層對象的屬性與全局變量是等價的。

window.a = 1;
a // 1

a = 2;
window.a // 2

上面代碼中,頂層對象的屬性賦值與全局變量的賦值,是同一件事。

總結:

該博客爲學習阮一峯 ES6入門課時所做的筆記記錄, 僅供留作筆記記錄學習和理解.

交流學習加 WeChat(備註技術交流學習):
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章