JavaScript 核心觀念(60) - ES6 章節:Let 及 Const - Let, Const 實戰運用技巧

前言

前一章節介紹了 let 與 const 的基本概念,那麼接下來就要講一下一些實戰上的應用。

Let, Const 實戰運用技巧

首先先來看一個非常經典的甚至是面試時常見的案例:

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

你可以先試著思考一下,雖然最外層的 i 我們預期是會出現 10,那麼 console.log('這次第' + i + '次執行'); 呈現出來會是什麼呢?

如果你對於 var 的作用域以事件佇列已經有一定掌握的話,相信你可以很快的講出答案,這邊也快速講一下這一段程式碼的運作邏輯。

首先迴圈將會執行十次,並且每次執行時都會設置一個計時器,因此將會有十個計時器,所以正常來講將會出現十次 這次第 xx 次執行 的訊息,但是實際上出現的結果卻是十次的 這次第 10 次執行 訊息

這次第 10 次執行

而這個原因主要是出在 var 的作用域是以函式為作用域,因此寫在 for loop 中的 var i = 0; i < 10; i++ 實際上是宣告在 window 底下,所以這是一個全域變數。

而裡面的 setTimeout 是一個非同步事件,因此就會被放到事件佇列中,等到所有行為執行完畢後(包含跑完迴圈到最外層的 console)再去執行事件佇列中 setTimeout 的事件,因此這一段程式碼所取得的 i 是全域變數的 i 而不是 for loop 中的 i,什麼意思呢?你可以試著改成以下程式碼理解試試看:

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

那麼如果期望的在 setTimeout 中輸出的結果是 這次第 0 次執行這次第 1 次執行…這種預期的狀況,那麼就可以使用 let 來解決,而 let 作用域是區塊作用域,因此該變數就只會在當前執行後的 block 中:

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

這也是為什麼你會出現一個「VM111:6 Uncaught ReferenceError: i is not defined」的錯誤訊息

i is not defined

而這一段的運作邏輯除了與 let 的作用域有關係之外,其中也包含了閉包的觀念,也就是記憶體被暫存在某一處,因為你預計會使用這個變數 i,因此這就形成了一個簡單的閉包,因為當迴圈每次執行之後,都會釋放裡面的記憶體,那麼因為我們有使用 setTimeout 指向 i 變數,所以這個記憶體就無法被釋放,這也是為什麼我們可以正常取得 i 的變數的原因囉。

const 的部分在前面章節有聊到是一個常數,也就是無法被重新賦予值,但是如果你今天 const 所指向的是一個物件,那麼你可能會看到這種狀況:

1
2
3
4
5
const obj = {
myName: 'Ray',
}

obj.myName = 'https://hsiangfeng.github.io/';

此時你可能會覺得很奇怪,為什麼沒有出現任何錯誤以及警告,這個原因在於我們 const 所指向的是物件的記憶體,而後面的 obj.myName 並沒有修改物件的記憶體位置,而是單純修改裡面的屬性而已,所以才沒有出現任何錯誤,因此這邊主要是物件傳參考的觀念。

除非你是重新賦予物件才會出現錯誤

1
2
3
4
5
6
7
const obj = {
myName: 'Ray',
}

obj.myName = 'https://hsiangfeng.github.io/';

obj = {};

Uncaught TypeError: Assignment to constant variable.

那麼這時候你可能會想說,除了固定物件記憶體位置之外,你也會希望物件中的屬性也是相同狀況,這時候你就可以使用物件章節的 Object.freeze 來凍結整個物件

1
2
3
4
5
6
7
8
const obj = {
myName: 'Ray',
}

Object.freeze(obj);

obj.myName = 'https://hsiangfeng.github.io/';
console.log(obj);

Object.freeze

這時候你可能會覺得很奇怪,JavaScript 不給修改但是卻沒有出現任何錯誤這個原因主要是靜默錯誤,因此你可以試著加入 'use strict'

1
2
3
4
5
6
7
8
9
'use strict'
const obj = {
myName: 'Ray',
}

Object.freeze(obj);

obj.myName = 'https://hsiangfeng.github.io/';
console.log(obj);

靜默錯誤

參考文獻

Liker 讚賞 (拍手)

如果這一篇筆記文章對你有幫助,希望可以求點支持或 牡蠣 鼓勵 (ノД`)・゜・。

Liker 是一個按讚(拍手)的讚賞機制,每一篇文章最多可以按五下(拍手),按讚過程你是完全不用付費的(除非你想要每個月贊助我 :D),你只需要登入帳號就可以開始按讚。
而 Liker 會依據按讚數量分配獎金給創作者,所以如果你願意按個讚我會非常感謝你唷。

Google AD

撰寫一篇文章其實真的很花時間,如果你願意「關閉 Adblock (廣告阻擋器)」來支持我的話,我會非常感謝你 ヽ(・∀・)ノ