var
、let
、const
都是宣告變數的方法,倘若沒有使用變數宣告,就會變成全域變數,那這些宣告的方法中,在作用域中的範圍是哪些,來記錄一下釐清觀念。
var let 的作用域差異
下方有一個範例,宣告後並透過一個立即函式來執行其內容,
1 | var mom = 'mary'; |
立即函式:IIFE (Immediately Invoked Function Expression) 是一個定義完馬上就執行的 JavaScript function。
可知會得到 mary
這個值,把 var
換成 let
也會有一樣的結果。
但若把 console.log
複製一個並移到最上面,結果會如何呢?
1 | console.log('mom') //mom is not defined |
會直接跳錯,並顯示:mom is not defined
,因為變數尚未定義,所以會找不到,但如果又把 let
換成 var
就會得到下方結果:
1 | console.log('mom') //undefined |
兩個都會被執行,不會跳錯,究竟 not defined
跟 undefined
的差別在哪裡呢? 這在開發時檢查會很常遇到,這邊紀錄一下。
undefined
記憶體已經準備好空間,但讀不到相關資料,之所以 var
可以抓到 undefined
,是 Hoisting 向上提升的特性。
Hoisting 向上提升:在變數與函數宣告時,會在編譯階段就被放入記憶體。
not defined
記憶體還沒有準備空間,所以沒辦法抓取資料到記憶體。
為了避免汙染全域,現在都會建議使用 let
。
var 與 let 的作用域
透過下方 function 的範例,來了解,var
與 let
的作用域範圍。
var 的作用域範圍是 function scope
若是在 function 內所宣告的 var
,皆會被影響,若重複宣告變數的值,後宣告的變數內容會去覆蓋這個 function 內先前所宣告的變數內容。
1 | function funA { |
let 的作用域範圍是 block
若是在 function 內使用 let
,只會讀取該「大括號」的內變數內容。
1 | function funA { |
由此可知,var
因為比較容易汙染到全域,所以使用 let
會比較容易管理 function 的內容。
var 與 let 經典的 for 迴圈範例
此範例希望出現的是在 console 內顯示 「這是第幾次執行」的文字,並且 i
的內容會是 0~9,所以預期的結果應該如下:
這是第0次執行
這是第1次執行
這是第2次執行
這是第3次執行
這是第4次執行
這是第5次執行
這是第6次執行
這是第7次執行
這是第8次執行
這是第9次執行
1 | for (var i = 0; i < 10; i++){ |
可看到一個 for 迴圈裏面有一個非同步事件,並且這個 for 迴圈並沒有被 function 包住,是直接執行的,邏輯上 setTimeout 的位置應該在 for 迴圈之外,如下程式碼:
1 | for (var i = 0; i < 10; i++){ |
透過第一範例知道這裡使用 var
宣告 i
的時候,此時 i
是全域變數,在 console.log
中輸入 window.i 時,會出現 10 的結果。所以 setTimeout 會執行的是全域變數底下的 i
,並不會依序的執行,所以這並非預期出現的結果。
但如果將 var
改成 let
,作用域就會變成 block,就只會執行該大括號內的內容。所以就會得到預期的結果,並且在 console 中輸入 window.i 結果會出現 undefined
。
const 的特性
1 | const a = 1; |
在開發人員工具,就會出現跳錯的訊息,
Uncaught SyntaxError: Identifier 'a' has already been declared
所以只要是不能變更的資訊,就可以使用 const
來宣告。(例如 AJAX 中的 API 網址。)
但是 const 宣告的物件是可以修改內容的
1 | const family = { |
因為 const
物件是傳參考,不是傳值,所以並非改變物件中的值,所以是可以的。
但如果把上方物件修改成: family = {};
因為修改了值,就會跳錯。
心得
了解 var
跟 let
作用域的概念以及 const
的特行,在使用 function 上,比較不會因為程式碼一多,就會搞亂。