深入探討 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 非常好用,可以讓程式碼更精簡。