前言
我們在前面幾篇文章中已經討論「執行環境」、「變數環境」、「詞彙環境」,然而這些東西最終定義了「範圍 (Scope)」。
範圍 (Scope)
範圍是變數可以被取用的區域,如果我們呼叫了相同的函式兩次,它們會各有一個屬於自己的執行環境,因此如果函式內有個同樣的變數被宣告兩次,該變數在記憶體中的位置是不一樣的。
上面那段話用例子來解釋的話是這樣:
1 | function a(){ |
「let」
在 ES6 (ECMAScript 6) 中,有個新的宣告變數的方法 let
,這個可以像 var
一樣使用,但並不是取代 var
, var
還是存在。
但 let
讓 JavaScript 使用了一種稱為「區塊範圍 (Block Scoping)」的東西,直接看一個片段程式碼:
1 | if (a > b) { |
我們可以宣告一個變數大致就像使用 var
一樣,變數一樣會被創造在執行階段、被放入記憶體中但不會被設值為 undefined
,較為不同的是,直到執行階段,這一行程式被執行、真的宣告變數後,才能存取用 let
宣告的值。
我們改寫一下上面的例子,如果試著在 let c = true
之前取用 變數 c
:
1 | var a = 10; |
會很直接的跳出 c is not defined
區塊範圍 (Block Scoping
區塊通常是被定義為在「大括號 {}」中,像是在 if
、 for
裡面,當變數被宣告在區塊內,變數就只能在裡面被取用。舉個區塊範圍及 let
的例子:
1 | for(var i = 0; i < 10 ; i++){ |
像是這個例子,每次進入迴圈執行到 let a = 0
時,變數 a
在記憶體中都是不同的存在,這就是區塊範圍。
「let」也有「提升 (hosting)」嗎?
以下引述自 胡立 大大寫的文章:我知道你懂 hoisting,可是你了解到多深?
老實說,我本來以為沒有,但實際上 let
也是有「提升 (hosting)」的,再來個例子:
1 | var a = 10 |
如果 let
沒有「提升」的話,這個範例的輸出應該會是「10」,因為 console.log(a)
會因為 test
函式內找不到變數 a
而跑到外部環境尋找。
但是!這範例的輸出是
ReferenceError: a is not defined
意思就是,它的確提升了,只是提升後的行為跟 var
比較不一樣,所以乍看之下你會以為它沒有提升。
與 var
的差別在於提升之後, var
宣告的變數會被初始化為 undefined
,而 let
的宣告不會被初始化為 undefined
,而且如果你在「賦值之前」就存取它,就會拋出錯誤。
在「提升之後」以及「賦值之前」這段「期間」,如果你存取它就會拋出錯誤,而這段期間就稱做是「TDZ」,它是一個為了解釋 let
的 「提升」行為所提出的一個名詞。
後續就到原文處當課後補充吧。
後記:
其實我在寫這篇文章的時候發現了一個衝突點,這讓我決定先上論壇尋求一下協助再來做修改,衝突點就是「 let
宣告的變數,會不會在創造階段內被放入記憶體中,且初始化為 undefined
」,影片之中的講師是說會,胡立的文章是說不會,不過這兩者時空背景不太一樣,所以要求證一下。
這是求證結果 ,因為是課程問答區可能沒有買的人不能看…。
簡單提一下結論:
let
/ const
宣告的變數,確實也有提升的作用,只是因為 TDZ 所以並不像使用 var
宣告會得到 undefined
,而是會直接拋出 ReferenceError
。
let
/ const
在創造階段時,也會把變數寫進記憶體,但是在此不會賦予預設值 undefined
,而是在之後的程式執行階段,如果以 let
/ const
宣告的變數沒有賦予值時,才會給予 undefined
,這時候 let
/ const
的初始化才算完成,此時 let
/ const
宣告的變數才能被正常使用。