JS-學習ES6之- let & const 關鍵字

1. 基本用法

let 爲塊級作用域,經典用法爲 for 循環

如果以下代碼使用 var,則最後輸出的是10

var a = [];
for (var i =1; i < 10; i++) {
    a[i] = function(){
        console.log(i);
    };
}
a[6](); // 10

如果使用 let,聲明的變量僅在塊級作用域內有效,最後輸出的是 6。

var a = [];
for (let i = 0; i < 10; i++) {
    a[i] = function(){
        console.log(i);
    };
}
a[6](); // 6

上面代碼中,變量 i 是 let 聲明的,當前的i只在本輪循環有效,所以每一次循環的i其實都是一個新的變量,所以最後輸出的是6。你可能會問,如果每一輪循環的變量i都是重新聲明的,那它怎麼知道上一輪循環的值,從而計算出本輪循環的值?這是因爲 JavaScript 引擎內部會記住上一輪循環的值,初始化本輪的變量i時,就在上一輪循環的基礎上進行計算。

另外,for 循環還有一個特別之處,就是設置循環變量的那部分是一個父作用域,而循環體內部是一個單獨的子作用域。

for(let i = 0; i < 3; i++){
    let i = 'abc';
    console.log(i);
}
// abc
// abc 
// abc

上面代碼正確運行,輸出了 3 次abc。這表明函數內部的變量i與循環變量 i 不在同一個作用域,有各自單獨的作用域。

2. 不存在變量提升

var 的情況

console.log(foo); //  輸出 undefined
var foo = 2;

let 的情況

console.log(bar); //  報錯ReferenceError
let bar = 2;

上面代碼中,變量 foo 用 var 命令聲明,會發生變量提升,即腳本開始運行時,變量 foo 已經存在了,但是沒有值,所以會輸出 undefined。變量 bar 用 let 命令聲明,不會發生變量提升。這表示在聲明它之前,變量bar是不存在的,這時如果用到它,就會拋出一個錯誤。

3. 暫時性死區

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

上面代碼中,存在全局變量tmp,但是塊級作用域內 let 又聲明瞭一個局部變量tmp,導致後者綁定這個塊級作用域,所以在 let 聲明變量前,對tmp賦值會報錯。ES6 明確規定,如果區塊中存在 letconst 命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。總之,在代碼塊內,使用 let 命令聲明變量之前,該變量都是不可用的。這在語法上,稱爲“暫時性死區”(temporal dead zone,簡稱 TDZ)。

有些“死區”比較隱蔽,不太容易發現。

function bar(x = y, y = 2) {
  return [x, y];
}

bar(); // 報錯

上面代碼中,調用bar函數之所以報錯(某些實現可能不報錯),是因爲參數x默認值等於另一個參數y,而此時y還沒有聲明,屬於”死區“。如果y的默認值是x,就不會報錯,因爲此時x已經聲明瞭。

4. 頂層對象的屬性

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

window.a = 1;
a // 1

a = 2;
window.a // 2

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

頂層對象的屬性與全局變量掛鉤,被認爲是 JavaScript 語言最大的設計敗筆之一。這樣的設計帶來了幾個很大的問題,首先是沒法在編譯時就報出變量未聲明的錯誤,只有運行時才能知道(因爲全局變量可能是頂層對象的屬性創造的,而屬性的創造是動態的);其次,程序員很容易不知不覺地就創建了全局變量(比如打字出錯);最後,頂層對象的屬性是到處可以讀寫的,這非常不利於模塊化編程。另一方面,window 對象有實體含義,指的是瀏覽器的窗口對象,頂層對象是一個有實體含義的對象,也是不合適的。

ES6 爲了改變這一點,一方面規定,爲了保持兼容性,var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;另一方面規定,let命令、const命令、class命令聲明的全局變量,不屬於頂層對象的屬性。也就是說,從 ES6 開始,全局變量將逐步與頂層對象的屬性脫鉤。

var a = 1;
// 如果在 Node 的 REPL 環境,可以寫成 global.a
// 或者採用通用方法,寫成 this.a
window.a // 1

let b = 1;
window.b // undefined

上面代碼中,全局變量 a 由 var 命令聲明,所以它是頂層對象的屬性;全局變量 b 由 let 命令聲明,所以它不是頂層對象的屬性,返回 undefined

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