[JavaScriptWeird]No.10 範圍與 let

前言

我們在前面幾篇文章中已經討論「執行環境」、「變數環境」、「詞彙環境」,然而這些東西最終定義了「範圍 (Scope)」。

範圍 (Scope)

範圍是變數可以被取用的區域,如果我們呼叫了相同的函式兩次,它們會各有一個屬於自己的執行環境,因此如果函式內有個同樣的變數被宣告兩次,該變數在記憶體中的位置是不一樣的。

上面那段話用例子來解釋的話是這樣:

1
2
3
4
5
6
function a(){  
var myVar = 1;
console.log(myVar);
}
a();
a();

「let」

在 ES6 (ECMAScript 6) 中,有個新的宣告變數的方法 let ,這個可以像 var 一樣使用,但並不是取代 varvar 還是存在。

let 讓 JavaScript 使用了一種稱為「區塊範圍 (Block Scoping)」的東西,直接看一個片段程式碼:

1
2
3
if (a > b) {  
let c = true;
}

我們可以宣告一個變數大致就像使用 var 一樣,變數一樣會被創造在執行階段、被放入記憶體中但不會被設值為 undefined ,較為不同的是,直到執行階段,這一行程式被執行、真的宣告變數後,才能存取用 let 宣告的值

我們改寫一下上面的例子,如果試著在 let c = true 之前取用 變數 c

1
2
3
4
5
6
var a = 10;  
var b = 5;
if (a > b) {
console.log(c);
let c = true;
}

會很直接的跳出 c is not defined

區塊範圍 (Block Scoping

區塊通常是被定義為在「大括號 {}」中,像是在 iffor 裡面,當變數被宣告在區塊內,變數就只能在裡面被取用。舉個區塊範圍及 let 的例子:

1
2
3
4
for(var i = 0; i < 10 ; i++){  
let a = 0;
console.log(a)
}

像是這個例子,每次進入迴圈執行到 let a = 0 時,變數 a 在記憶體中都是不同的存在,這就是區塊範圍。

「let」也有「提升 (hosting)」嗎?

以下引述自 胡立 大大寫的文章:我知道你懂 hoisting,可是你了解到多深?

老實說,我本來以為沒有,但實際上 let 也是有「提升 (hosting)」的,再來個例子:

1
2
3
4
5
6
var a = 10  
function test(){
console.log(a)
let a
}
test()

如果 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 宣告的變數才能被正常使用。

0%