JavaScript 核心觀念(60) - ES6 章節:Let 及 Const - Let, Const 實戰運用技巧
前言
前一章節介紹了 let 與 const 的基本概念,那麼接下來就要講一下一些實戰上的應用。
Let, Const 實戰運用技巧
首先先來看一個非常經典的甚至是面試時常見的案例:
1 | for(var i = 0; i < 10; i++) { |
你可以先試著思考一下,雖然最外層的 i
我們預期是會出現 10
,那麼 console.log('這次第' + i + '次執行');
呈現出來會是什麼呢?
如果你對於 var
的作用域以事件佇列已經有一定掌握的話,相信你可以很快的講出答案,這邊也快速講一下這一段程式碼的運作邏輯。
首先迴圈將會執行十次,並且每次執行時都會設置一個計時器,因此將會有十個計時器,所以正常來講將會出現十次 這次第 xx 次執行
的訊息,但是實際上出現的結果卻是十次的 這次第 10 次執行
訊息
而這個原因主要是出在 var
的作用域是以函式為作用域,因此寫在 for loop 中的 var i = 0; i < 10; i++
實際上是宣告在 window 底下,所以這是一個全域變數。
而裡面的 setTimeout
是一個非同步事件,因此就會被放到事件佇列中,等到所有行為執行完畢後(包含跑完迴圈到最外層的 console
)再去執行事件佇列中 setTimeout
的事件,因此這一段程式碼所取得的 i
是全域變數的 i
而不是 for loop 中的 i
,什麼意思呢?你可以試著改成以下程式碼理解試試看:
1 | for(var i = 0; i < 10; i++) { |
那麼如果期望的在 setTimeout
中輸出的結果是 這次第 0 次執行
、這次第 1 次執行
…這種預期的狀況,那麼就可以使用 let
來解決,而 let
作用域是區塊作用域,因此該變數就只會在當前執行後的 block 中:
1 | for(let i = 0; i < 10; i++) { |
這也是為什麼你會出現一個「VM111:6 Uncaught ReferenceError: i is not defined
」的錯誤訊息
而這一段的運作邏輯除了與 let
的作用域有關係之外,其中也包含了閉包的觀念,也就是記憶體被暫存在某一處,因為你預計會使用這個變數 i
,因此這就形成了一個簡單的閉包,因為當迴圈每次執行之後,都會釋放裡面的記憶體,那麼因為我們有使用 setTimeout
指向 i
變數,所以這個記憶體就無法被釋放,這也是為什麼我們可以正常取得 i
的變數的原因囉。
而 const
的部分在前面章節有聊到是一個常數,也就是無法被重新賦予值,但是如果你今天 const
所指向的是一個物件,那麼你可能會看到這種狀況:
1 | const obj = { |
此時你可能會覺得很奇怪,為什麼沒有出現任何錯誤以及警告,這個原因在於我們 const
所指向的是物件的記憶體,而後面的 obj.myName
並沒有修改物件的記憶體位置,而是單純修改裡面的屬性而已,所以才沒有出現任何錯誤,因此這邊主要是物件傳參考的觀念。
除非你是重新賦予物件才會出現錯誤
1 | const obj = { |
那麼這時候你可能會想說,除了固定物件記憶體位置之外,你也會希望物件中的屬性也是相同狀況,這時候你就可以使用物件章節的 Object.freeze
來凍結整個物件
1 | const obj = { |
這時候你可能會覺得很奇怪,JavaScript 不給修改但是卻沒有出現任何錯誤這個原因主要是靜默錯誤,因此你可以試著加入 'use strict'
1 |
|