深入探討 TypeScript 之變數宣告與建立
變數宣告部分,除了 var
以外,在 ES6 增加了 let
與 const
,在變數建立部分,則增加了 destructuring 與 spread,當然 TypeScript 也完全支援。
Version
TypeScript 2.3
var
1 | for(var i = 0; i < 10; i++) { |
var
最經典的範例莫過於此,原本我們預期會輸出
1 | 0 |
結果輸出
1 | 10 |
這裡有兩個觀念:
setTimeout()
屬於非同步 function,JavaScript 的 event loop model 會在最後執行。var
看似在{}
內,但沒有 scope 概念,因為其 hoisting 機制,會將var
提升到程式最前面,相當於全域變數,因此console.log()
印出 for loop 執行完的10
。
let
1 | for(let i = 0; i < 10; i++) { |
將 var
改成 let
之後,結果就如預期了。
1 | 0 |
let
對變數有 scope 概念,其生命週期只存在於 {}
,因此儘管非同步的 setTimeout()
最後執行,let 仍將 i
鎖住到最後,而不像 var
沒有 scope 概念,都是全域變數。
實務上應該全面使用 let 取代 var,若你使用 var,language service 也會提出警告,要你改用 let。
const
1 | const numLivesForCat = 9; |
若此變數不會再被修改,則應宣告成 const。
傳統我們都是確定此變數不能被修改,才會宣告成 const,但在 TypeScript 中是反過來,變數應該盡量宣告成 const,除非要修改才宣告成 let,事實上,若 language service 發現變數從來沒被修改過,會主動提出警告,要求你改成 const,這是希望大家盡量寫出 immutable 的 pure function,減少不必要的 side effect。
Destructuring
Array Destructing
ES6 允許我們直接將陣列加以解構成變數。
1 | var input = [1, 2]; |
在 ES5,我們必須使用 array index 方式,才能將值指定到變數。
1 | let input = [1, 2]; |
ES6 我們可以直接使用 []
將陣列加以解構。
1 | // swap variables |
交換變數,只要一行就可以寫出來。
1 | function f([first, second]: number[]) { |
若用在 function 的參數,可以接陣列解構成變數。
1 | let [first, ...rest] = [1, 2, 3, 4]; |
配合 …
,將剩餘的值結構成陣列。
first
為單一變數,rest
為陣列。
1 | let [first] = [1, 2, 3, 4]; |
first
為 1
,其他值會忽略。
1 | let [, second, , fourth] = [1, 2, 3, 4]; |
只有 2
與 4
會解構,其他值會忽略。
Object Destructing
除了解構陣列外,ES6 還允許我們解構物件。
1 | let o = { |
c
因為沒有變數解構,會自動忽略。
1 | let o = { |
配合 …
,將剩餘的值結構成物件。
a
為單一變數,passthrough
為物件。
1 | type C = { a: string, b: number }; |
若用在 function 的參數,可以接物件解構成變數。
Spread
1 | let first = [1, 2]; |
…
會將陣列加以展開。
bothPlus
為 [0, 1, 2, 3, 4, 5]
。
1 | let defaults = { food: "spicy", price: "$$", ambiance: "noisy" }; |
…
會將物件屬性加以展開。
search
為 { food: "rich", price: "$$", ambiance: "noisy" }
若屬性有重複,將後面蓋前面。
1 | class C { |
…
只能展開 property,不能展開 method。
Conclusion
- 實務上應該盡量使用
const
,除非變數需要被修改才用let
。 - 盡量寫出 pure function 減少 side effect。
- Destructuring 與 spread 非常好用,可以讓程式碼更精簡。