ES6標準入門之---let與const

let 與 const,爲年輕時犯下的「錯誤」買單!

在講解es6以前,咱們必需要提一下es5中的var,也就是曾經的那個錯誤。es6

if (condition) {
    var value = 1;
}
console.log(value);
複製代碼

很簡單的分析一下,初學者可能會認爲,只有在condition爲true的時候,value纔會被賦值,若是condition爲false的時候,代碼應該會報錯纔對!可是,事實並非這樣的,瀏覽器在執行這段代碼的時候,並非這麼解析的!瀏覽器

var value;
if (condition) {
    value = 1;
}
console.log(value);
複製代碼

這樣,很容易咱們就能判斷出,若是condition的值爲false的時候,console出來的值爲undefined。bash

緣由就是咱們常說的,變量提高閉包

爲了增強對變量生命週期的控制,ECMAScript 6 引入了塊級做用域。函數

塊級做用域存在於:post

  • 函數內部ui

  • 塊中(字符 { 和 } 之間的區域)es5

引出咱們今天的主角—let和const

let和const的特色:spa

1.不會被提高(真的是這樣的嗎?)3d

if (condition) {
    let value = 1;
}
console.log(value); // Uncaught ReferenceError: value is not defined
複製代碼

在代碼塊外面訪問,直接斷定,未定義。

2.重複聲明報錯

var value = 1;
let value = 2; // Uncaught SyntaxError: Identifier 'value' has already been declared
複製代碼

這在之前是徹底能夠的,後定義的回覆蓋之前的。

3.不綁定全局做用域

let value = 1 ;
console.log(window.value); 
複製代碼

const也是相同的,都訪問不到。

const和let的區別:

const 用於聲明常量,其值一旦被設定不能再被修改,不然會報錯。

值得一提的是:const 聲明不容許修改綁定,但容許修改值。這意味着當用 const 聲明對象時:

const data = {
    value: 1
}

// 沒有問題
data.value = 2;
data.num = 3;

// 報錯
data = {}; // Uncaught TypeError: Assignment to constant variable.
複製代碼

臨時死區

臨時死區(Temporal Dead Zone),簡寫爲 TDZ。

let 和 const 聲明的變量不會被提高到做用域頂部,若是在聲明以前訪問這些變量,會致使報錯。

console.log(typeof value); // Uncaught ReferenceError: value is not defined
let value = 1;
複製代碼

來個例子~

var value = "global";
// 例子1
(function() {
    console.log(value);
    let value = 'local';
}());

// 例子2
{
    console.log(value);
    const value = 'local';
};
複製代碼

結果是:都報錯了~!

這是由於 JavaScript 引擎在掃描代碼發現變量聲明時,要麼將它們提高到做用域頂部(遇到 var 聲明),要麼將聲明放在 TDZ 中(遇到 let 和 const 聲明)。訪問 TDZ 中的變量會觸發運行時錯誤。只有執行過變量聲明語句後,變量纔會從 TDZ 中移出,而後方可訪問。 循環中的塊級做用域:

var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // 3
複製代碼

如何改變現狀呢?我要的是funcs0 == 0

在沒有es6 以前,這個事兒麻煩了,還得使用閉包的方式!

var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs[i] = (function(i){
        return function() {
            console.log(i);
        }
    }(i))
}
funcs[0](); // 0
複製代碼

ES6 的 let 爲這個問題提供了新的解決方法:

var funcs = [];
for (let i = 0; i < 3; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // 0
複製代碼

問題在於,上面講了 let 不提高,不能重複聲明,不能綁定全局做用域等等特性,但是爲何在這裏就能正確打印出 i 值呢? 若是是不重複聲明,在循環第二次的時候,又用 let聲明瞭i,應該報錯呀,就算由於某種緣由,重複聲明不報錯,一遍一遍迭代,i 的值最終仍是應該是 3 呀,還有人說 for 循環的 設置循環變量的那部分是一個單獨的做用域,就好比:

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

這個例子是對的,若是咱們把 let 改爲 var 呢?

for (var i = 0; i < 3; i++) {
  var i = 'abc';
  console.log(i);
}
// abc
複製代碼

經查, for 循環中使用 let 和 var,底層會使用不一樣的處理方式。 簡單的來講,就是在 for (let i = 0; i < 3; i++) 中,即圓括號以內創建一個隱藏的做用域,這就能夠解釋爲何:

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

而後每次迭代循環時都建立一個新變量,並以以前迭代中同名變量的值將其初始化。

var funcs = [];
for (let i = 0; i < 3; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // 0
複製代碼

至關於:

// 僞代碼
(let i = 0) {
    funcs[0] = function() {
        console.log(i)
    };
}

(let i = 1) {
    funcs[1] = function() {
        console.log(i)
    };
}

(let i = 2) {
    funcs[2] = function() {
        console.log(i)
    };
};

複製代碼

到此,咱們就講完了嗎?沒有,並無,上面還有個提高的問題麼!

首先明確一點:提高不是一個技術名詞。

要搞清楚提高的本質,須要理解 JS 變量的 這就解釋了爲何在 let x 以前使用 x 會報錯:

假設有以下代碼:

function fn(){
  var x = 1
  var y = 2
}
fn()
複製代碼

在執行 fn 時,會有如下過程(不徹底):

  • 進入 fn,爲 fn 建立一個環境。

  • 找到 fn 中全部用 var 聲明的變量,在這個環境中「建立」這些變量(即 x 和 y)。

  • 將這些變量「初始化」爲 undefined。

  • 開始執行代碼

  • x = 1 將 x 變量「賦值」爲 1

  • y = 2 將 y 變量「賦值」爲 2

也就是說 var 聲明會在代碼執行以前就將「建立變量,並將其初始化爲 undefined」。

這就解釋了爲何在 var x = 1 以前 console.log(x) 會獲得 undefined。

接下來來看 function 聲明的「建立、初始化和賦值」過程

假設代碼以下:

fn2()

function fn2(){
  console.log(2)
}
複製代碼

JS 引擎會有一下過程:

  • 找到全部用 function 聲明的變量,在環境中「建立」這些變量。

  • 將這些變量「初始化」並「賦值」爲 function(){ console.log(2) }。

  • 開始執行代碼 fn2()

也就是說 function 聲明會在代碼執行以前就「建立、初始化並賦值」

接下來看 let 聲明的「建立、初始化和賦值」過程

{
  let x = 1
  x = 2
}
複製代碼

咱們只看 {} 裏面的過程:

  • 找到全部用 let 聲明的變量,在環境中「建立」這些變量

  • 開始執行代碼(注意如今尚未初始化)

  • 執行 x = 1,將 x 「初始化」爲 1(這並非一次賦值,若是代碼是 let x,就將 x 初始化爲 undefined)

  • 執行 x = 2,對 x 進行「賦值」

let x = 'global'
{
  console.log(x) // Uncaught ReferenceError: x is not defined   
  let x = 1
}
複製代碼

這就解釋了爲何在 let x 以前使用 x 會報錯: 緣由有兩個:

  • console.log(x) 中的 x 指的是下面的 x,而不是全局的 x

  • 執行 log 時 x 還沒「初始化」,因此不能使用(也就是所謂的暫時死區)

看到這裏,你應該明白了 let 到底有沒有提高:

  • let 的「建立」過程被提高了,可是初始化沒有提高。

  • var 的「建立」和「初始化」都被提高了。

  • function 的「建立」「初始化」和「賦值」都被提高了。

最後看 const,其實 const 和 let 只有一個區別,那就是 const 只有「建立」和「初始化」,沒有「賦值」過程!!!

這四種聲明,用下圖就能夠快速理解:

所謂暫時死區,就是不能在初始化以前,使用變量。

感謝:zhuanlan.zhihu.com/p/28140450 juejin.im/post/5b0238…

相關文章
相關標籤/搜索