亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

ES6+ let

1. 前言

本節我們一起學習下 ES6 中的 let,在 ES5 中變量的方法只有一個 var ,但是使用 var 來定義的變量存在很多缺陷和弊端,ES6 引入了 let 語句來聲明變量,同時引入了很多概念,比如塊級作用域、暫存死區等等。限制了任意聲明變量,提升了程序的健壯性。

2. 基本用法

let 的使用方法類似于 var,并可以代替 var 來聲明變量。

{
  let name = 'imooc';
}

let 允許你聲明一個作用域被限制在塊級中的變量、語句或者表達式。與 var 關鍵字不同的是,var 聲明的變量只能在全局或者整個函數塊中。 varlet 的不同之處在于 let 是在編譯時才初始化,也就是在同一個塊級下不能重復地聲明一個變量,否則會報錯。

let 不會在全局聲明時創建 window 對象的屬性,但是 var 會。

{
  var name = 'imooc'  // imooc
  var name = 'iimooc' // iimooc 
}
console.log(window.name)	// iimooc
{
  let age = 10        // 10
  let age = 18        // Uncaught SyntaxError: Identifier 'age' has already been declared 
}
console.log(window.age)		// undefined

上面的代碼中,在一個塊中分別使用 varlet 來聲明變量對比他們之間的差異,從上面的代碼操作可以看出,我們可以使用 var 多次對 name 聲明,但是使用 let 聲明的 age,后面再使用 let 對其聲明是會報錯的。
var 是沒有塊的概念的,聲明的變量會是 window 對象上的屬性,在最外層的 window 上可以取到。而 let 存在塊的概念,不會添加到 window 對象上,這些是 let 和 var 之間的區別。從這里我們可以了解到為什么使用 let

3. 塊級作用域

在深入了解 let 前,我們需要了解一下,在 JavaScript 中有哪些作用域:

  1. 全局作用域
  2. 函數作用域 / 局部作用域
  3. 塊級作用域

上面是 JavaScript 中的三種作用域,那什么是作用域呢?首先要明白的是:幾乎所有的編程語言都存在在變量中儲值的能力,存儲完就需要使用這些值。所以,作用域就是一套規則,按照這套規則可以方便地去存儲和訪問變量。

在 ES5 中的作用域有全局作用域和函數作用域,而塊級作用域是 ES6 的概念。

3.1 全局作用域

全局作用域顧名思義,就是在任何地方都能訪問到它,在瀏覽器中能通過 window 對象拿到的變量就是全局作用域下聲明的變量。

var name = 'imooc';
console.log(window.name)   // imooc

使用 var 定義的變量,可以在 window 對象上拿到此變量。這里的 name 就是全局作用域下的變量。

3.2 函數作用域

函數作用域就是在函數內部定義的變量,也就是局部作用域,在函數的外部是不能使用這個變量的,也就是對外是封閉的,從外層是無法直接訪問函數內部的作用域的。

function bar() {
  var name = 'imooc';
}
console.log(name);  // undefined

在函數內部定義的 name 變量,在函數外部是訪問不了的。要想在函數外部訪問函數內部的變量可以通過 return 的方式返回出來。

function bar(value) {
  var name = ' imooc';
  return value + name;
}
console.log(bar('hello'));  // hello imooc

借助 return 執行函數 bar 可以取到函數內部的變量 name 的值進行使用。

3.3 塊級作用域

塊級作用域是 ES6 的概念,它的產生是要有一定的條件的,在大括號({})中,使用 letconst 聲明的變量,才會產生塊級作用域。
這里需要注意的是,塊級作用域的產生是 letconst 帶來的,而不是大括號,大括號的作用是限制 letconst 的作用域范圍。當不在大括號中聲明時, letconst 的作用域范圍是全局。

let name = 10;
console.log(window.name)   // undefined

上面的代碼可以看到,使用 let 方式聲明的變量在 window 下是取不到的。

var num = 10;
{
  var num = 20;
  console.log(num)  // 20
}
console.log(num)    // 20

在使用 var 聲明的情況下,可以看出,外層的 num 會被 {} 中的 num 覆蓋,所以沒有塊級作用域的概念,下面看下使用 let 方式聲明:

let num = 10;
{
  console.log(num); // Uncaught ReferenceError: Cannot access 'num' before initialization
  let num = 20;
  console.log(num)  // 20
}
console.log(num)    // 10

這里可以看出 {} 內外是互不干涉和影響的,如果在聲明 num 的前面進行打印的話,還會報錯,這個時候,num 處于暫存死區,是不能被使用的,下面我們會具體說明。

在低版本瀏覽器中不支持 ES6 語法,通常需要把 ES6 語法轉換成 ES5,使用 babel 把上面的代碼轉換后得到如下結果:

var num = 10;
{
  console.log(_num); // num is not defined
  var _num = 20;
  console.log(_num); // 20
}
console.log(num);    // 10

從上面的代碼中可以看出,雖然在 ES6 語法使用的是相同的變量名字,但是底層 JS 進行編譯時會認為他們是不同的變量。也就是說即使大括號中聲明的變量和外面的變量是相同的名字,但是在編譯過程它們是沒有關系的。

塊級作用域可以任意嵌套,如下實例:

{{
  let x = 'Hello imooc'
  {
    console.log(x); // Hello imooc
    let y = 'Hello World'
  }
  console.log(y);   // 引用錯誤 ReferenceError: y is not defined.
}};

上方代碼每一層都是一個單獨的作用域,內層作用域可以讀取外層的變量,所以第一個會打印 Hello imooc, 而外層無法讀取內層的變量,所以會報錯。

4. 不能變量提升

對應 var 我們知道可以變量提升的,提升到作用域的最頂部,作用域是全局,使得聲明變量前也可以使用,但值為 undefined。

{
  console.log(bar); // 輸出undefined,沒有值但不會報錯
  var bar = 1;
}

一般變量都應該先聲明再使用,所以 letconst 語法規定必須聲明后使用,否則報錯。

{
  console.log(name); // 引用錯誤 ReferenceError: name is not defined.
  let name = 'imooc';
}

上面代碼中,都是在聲明前的時候使用變量的,這時候由于 let 不能進行變量提升所以會報引用錯誤。

5. 暫時性死區

上面講到在變量聲明前使用這個變量,就會報錯。在代碼塊內,使用 let 命令聲明變量之前,該變量都是不可用的。這在語法上,稱為 “暫時性死區”(temporal dead zone,簡稱 TDZ)。

{
  console.log(name);  // ReferenceError: name is not defined. 
  let name = 'imooc';
  console.log(name);  // imooc
}

上面代碼中,在塊中使用了 let 聲明了 name 變量,在使用前對 name 進行了聲明,name 則會處于暫存死區,不能被使用,如果引用則會引用錯誤。

Tips:注意對于 typeof 也是會報錯的。

{
  console.log(typeof name);  // Uncaught ReferenceError: Cannot access 'name' before initialization
  let name = 'imooc';
}

上面的代碼中,name 引用錯誤:無法在初始化之前訪問 name,因為 name 在這個塊的下面進行了聲明,name 就是一個死區,不能被引用了。因此,typeof 運行時就會拋出一個 ReferenceError 的參數錯誤。

6. 重復聲明報錯

let 不允許在同一個函數或塊作用域中重復聲明同一個變量,否則會引起語法錯誤(SyntaxError)。

{
  let x = 10;
  let x = 11;
}
// Uncaught SyntaxError: Identifier 'x' has already been declared

在上面的代碼中報錯,所以,同一個變量名不可以在同一個作用域內重復聲明。

{
  let x = 10;
  var x = 1;
}

即使使用 var 去聲明也是不可以的,我們知道當使用 let 聲明的時候 x 已經是一個死區了,不可以被重復聲明了。

Tips:注意在 switch 語句中只有一個塊級作用域,所以下面這種情況也是會報錯的。

let x = 1;
switch(x) {
  case 0:
    let num;
    break;
    
  case 1:
    let num;//重復聲明了
    break;
}
// 報錯

如果把 case 后面的語句放到塊作用域中則不會報錯。

let x = 1;
switch(x) {
  case 0: {//塊
    let num;
    break;
  } 
  case 1: {//塊
    let num;//這里就沒有關系了,可以正常聲明
    break;
  }
}

上方代碼,case 后面的語句 let 變量聲明在放到塊中,是單獨的作用域,所以就不會報錯。

7. 小結

本節講解了 let 語句的使用,還有作用域的概念,需要注意以下幾點:

  1. let 只作用于塊級作用域內;
  2. let 聲明的變量不能進行變量提升,存在暫存死區;
  3. let 聲明的變量不允許重復聲明,無論重復用 var 或者其他聲明都不行;
  4. 盡量使用 let 去代替 var 來聲明變量。