0%

JS 筆記 - ES6:使用 let 與 const 宣告變數

ES6

varletconst 都是宣告變數的方法,倘若沒有使用變數宣告,就會變成全域變數,那這些宣告的方法中,在作用域中的範圍是哪些,來記錄一下釐清觀念。

var let 的作用域差異

下方有一個範例,宣告後並透過一個立即函式來執行其內容,

1
2
3
4
var mom = 'mary';
(function(){
console.log('mom') // mary
})();

立即函式:IIFE (Immediately Invoked Function Expression) 是一個定義完馬上就執行的 JavaScript function。

可知會得到 mary 這個值,把 var 換成 let 也會有一樣的結果。
但若把 console.log 複製一個並移到最上面,結果會如何呢?

1
2
3
4
5
console.log('mom') //mom is not defined
let mom = 'mary';
(function(){
console.log('mom')
})();

會直接跳錯,並顯示:mom is not defined,因為變數尚未定義,所以會找不到,但如果又把 let 換成 var 就會得到下方結果:

1
2
3
4
5
console.log('mom') //undefined
var mom = 'mary';
(function(){
console.log('mom') // mary
})();

兩個都會被執行,不會跳錯,究竟 not definedundefined 的差別在哪裡呢? 這在開發時檢查會很常遇到,這邊紀錄一下。

undefined

記憶體已經準備好空間,但讀不到相關資料,之所以 var 可以抓到 undefined,是 Hoisting 向上提升的特性。

Hoisting 向上提升:在變數與函數宣告時,會在編譯階段就被放入記憶體。

not defined

記憶體還沒有準備空間,所以沒辦法抓取資料到記憶體。

為了避免汙染全域,現在都會建議使用 let

var 與 let 的作用域

透過下方 function 的範例,來了解,varlet 的作用域範圍。

var 的作用域範圍是 function scope

若是在 function 內所宣告的 var,皆會被影響,若重複宣告變數的值,後宣告的變數內容會去覆蓋這個 function 內先前所宣告的變數內容。

1
2
3
4
5
6
7
function funA {
var name = '小明';
if(true){
var name = '大明';
}
console.log (name); // '大明'
}

let 的作用域範圍是 block

若是在 function 內使用 let ,只會讀取該「大括號」的內變數內容。

1
2
3
4
5
6
7
function funA {
let name = '小明';
if(true){
let name = '大明';
}
console.log (name); // '小明'
}

由此可知,var 因為比較容易汙染到全域,所以使用 let 會比較容易管理 function 的內容。

var 與 let 經典的 for 迴圈範例

此範例希望出現的是在 console 內顯示 「這是第幾次執行」的文字,並且 i 的內容會是 0~9,所以預期的結果應該如下:

這是第0次執行
這是第1次執行
這是第2次執行
這是第3次執行
這是第4次執行
這是第5次執行
這是第6次執行
這是第7次執行
這是第8次執行
這是第9次執行

1
2
3
4
5
6
for (var i = 0; i < 10; i++){
console.log(i);
setTimeout (function(){
console.log('這是第'+i+'次執行')
},10);
}

可看到一個 for 迴圈裏面有一個非同步事件,並且這個 for 迴圈並沒有被 function 包住,是直接執行的,邏輯上 setTimeout 的位置應該在 for 迴圈之外,如下程式碼:

1
2
3
4
5
6
for (var i = 0; i < 10; i++){
console.log(i);
}
setTimeout (function(){ //應該在 i 加總完之後,才在這裡執行
console.log('這是第'+i+'次執行')
},10);

透過第一範例知道這裡使用 var 宣告 i 的時候,此時 i 是全域變數,在 console.log 中輸入 window.i 時,會出現 10 的結果。所以 setTimeout 會執行的是全域變數底下的 i,並不會依序的執行,所以這並非預期出現的結果。

但如果將 var 改成 let,作用域就會變成 block,就只會執行該大括號內的內容。所以就會得到預期的結果,並且在 console 中輸入 window.i 結果會出現 undefined

const 的特性

1
2
3
const a = 1;
a = 2;
console.log(a); //2

在開發人員工具,就會出現跳錯的訊息,

Uncaught SyntaxError: Identifier 'a' has already been declared

所以只要是不能變更的資訊,就可以使用 const 來宣告。(例如 AJAX 中的 API 網址。)

但是 const 宣告的物件是可以修改內容的

1
2
3
4
5
6
7
8
9
10
11
12
13
const family = {
mom: '媽媽',
son: '兒子',
sister: '妹妹'
};
family.dad = '爸爸';
console.log(family);

//就會得到
//mom: "媽媽"
//son: "兒子"
//sister: "妹妹"
//dad: "爸爸"

因為 const 物件是傳參考,不是傳值,所以並非改變物件中的值,所以是可以的。
但如果把上方物件修改成: family = {};
因為修改了值,就會跳錯。

心得

了解 varlet 作用域的概念以及 const 的特行,在使用 function 上,比較不會因為程式碼一多,就會搞亂。