Variable Decarations
let
和 const
是兩種 Javascript 新的宣告方式, let
和 var
比較類似
const
則是定義之後禁止之後修改(常數)
TypeScript
也有提供 let
和 const
的宣告方式,下個部分將會解釋為什麼會推薦使用 let
和 const
var 宣告
在 Javascript 中宣告一個變數常用的方式是
1 | var a = 10; |
在上面的例子之中,你宣告了一個變數 a
為 10
你也可以再 Function 中宣告
1 | function f(){ |
也可以允許相同的變數在不同的 Function scope
1 | function f(){ |
在上方的範例中 g()
中可以取得變數 a
得值
1 | function f(){ |
使用 var 宣告會有一些區域的規則問題
1 | function f(shouldInitialize: boolean){ |
因為 var
是在 if裡面,所以當 shouldInitialize
是 false 的話就部會執行 if裡面的程式碼
所以 x 並未宣告過,就會造成 undefined
這個規則可能會造成壹些不同種類型的錯誤,其中一種就是當你重複宣告同樣名稱的變數的時候彼此會互相覆蓋
1 | function sumMatrix(matrix: number[][]){ |
上述範例就可以發現因為 i
變數在雙迴圈中會被互相覆蓋造成程式執行上的錯誤,不會依據我們預想的去執行
奇怪的問題
1 | for(var i=0;i<10;i++){ |
但是結過卻是
1 | 10 |
但是我們希望的是
1 | 0 |
因為每次呼叫 setTimeout
會延遲一段時間後才開始執行 Function但是迴圈會不斷覆蓋掉 i
這個變數,而在延遲時間之後呼叫到的 i
則是最後覆蓋成 10 的 i
最常見要解決這件事情的方式如下
1 | for(var =0; i< 10; i++){ |
這個看起來有點奇怪的解決方式在 javascript 中卻是常見解決這個問題的方式
let
現在你已經知道 var
會有一些問題,所以會有一些問題,所以為什麼需要介紹 let
.let
和 var
的使用方式依樣
1 | let hello = 'hello'; |
Block-scoping
使用 let
宣告的時候,它的作用域市 blocking-scope
。和 var
宣告的作用域不一樣,他是用大括號來做區隔
1 | function f(input: boolean){ |
上述範例中有 a
和 b
兩個變數, a
的變數範圍在整個 f()
Function 之中,而 b
只會存在 if
之中
而變數使用 try catch 宣告的範例如下
1 | try{ |
另外一個很重要的 blocking-scope
變數不能在宣告之前做任何動作
1 | a++; |
在 TypeScript
中對這樣的提前宣告較為寬鬆,你需要使用 try catch
來取得錯誤訊息
若是沒有使用 try catch
TypeScrtip
並不會顯示這個訊息,若是在 ES2015
則會顯示這個錯誤訊息
1 | function foo(){ |
重複宣告和 shadowing
若是使用 var
的方式來宣告的話,他不會在意你宣告過幾次
1 | function f(x){ |
使用 let
宣告在同一個 scope 中只能宣告一次
1 | let x = 10; |
1 | function f(x){ |
只要是在不同的 blocking-scope
就可以做同名的宣告
1 | function (condition, x){ |
宣告一個新的名稱在另外一個內嵌的 block-scoping
這個行為叫做 shadowing
,但是這樣的行為會造成一些 bugs
例如:
1 | function sumMatrix(matrix: number[][]){ |
shadowing
在攥寫程式碼的時候應該要避免的狀況之一
Block-scoped variable capturing
當我們在一個作用域中宣告一個變數與 Function ,而 Function 也是其中一個作用域,在這個 Function 使用已宣告的變數的時候,即使脫離了那個作用域,也是依舊可以使用該變數
1 | function theCityThatAlwaysSleeps(){ |
因為 city
雖然是在 if
的作用域宣告的,但是可以透過 Function 記住他的指標
即使脫離作用域之後也可以透過該 Function 做呼叫使用
回憶之前 setTimeout
的範例, let
有相當大程度的不同
1 | for(let i=0; i < 10; i++>){ |
結果為
1 | 0 |
const
const
是另外一種不同的宣告
1 | const numLivesForCat = 9; |
雖然看起來跟 let
宣告一樣,雖然他們有相同的 block-scoping
規則,但是還是有些不同
const
宣告的變數是 immutable
的
1 | const numLivesForCat = 9; |
除非你要整個複寫整個物件,否則還是可以修改參數值得,
也就是此物件性質為 唯讀
的,詳情參閱
let vs. const
為什麼需要兩個不同的語意卻擁有相同的 block-scoping
的宣告方式呢?
基於 最小權限原則
若之後變數都不需要修改或是物件僅僅提供修改參數的權限時,則使用 const
,換句話說若是變數之後有可能會被覆寫則使用 let
來宣告
Destructuring
ES2015
的特性在 Typescript
中依舊可以使用
Array destructuring
1 | let input = [1, 2]; |
這個解構也可以在 Function 中使用
1 | function f([first, second]: [number, number]){ |
也可以將大量的變數指定給某一個變數
1 | let [first, ...rest] = [1, 2, 3, 4]; |
當然你也可抵應某些參數
1 | let [first] = [1, 2, 3, 4]; |
也可以單存指定某些特定的參數
1 | let [, second, , fourth ] = [1, 2, 3, 4]; |
Object destructuring
你也可以解構 object
1 | let o = { |
你可以單純指定 a
, b
而 c
可以因為不使用而跳過
你也可以使用 ...
這個形態來指定變數
1 | let {a, ...passthrogh} = o; |
property renaming
你可以對變數重新命名
1 | let {a: newName1, b: newName2} = o; |
也可以將 a:newName1
改為 a as newName1
也是一樣的效果
在 TypeScript
也是需要宣告類型
1 | let {a, b}: {a: string, b: number} = o; |
Default vaules
在你解構的時候也可以提供預設值
1 | function keepWholeObject({a: string, b?: number}){ |
keepWholeObject
中的 b
若是有參數則指定該參數,若是 undefined
或是 null
則指定為預設值 1001
宣告 Function
解構依舊可以使用在宣告函式之中
1 | type C = {a: string, b?:number}; |
也可以解構時預先放入預設值
1 | function f({a, b} = {a: "", b: 0}): void{ |
參數與設定型態都可以給予預設值,但是這兩個又有什麼不同呢?
1 | function({a, b=0}: {a: ""}): void{ |
Spread
ES2015
中的 Spread
特性也是支援的
1 | let first = [1, 2]; |
Spread
也可以使用在 object
1 | let defaults = {food: 'spicy', price: '$$', ambiance: 'noisy'}; |
在上述範例中 search
會解構 defaults
而且後面的 food 因為和 defaults
中重複而且順序在 defaults
的後面,所以會被覆蓋
1 | let defaults = {food: 'spicy', price: '$$', ambiance: 'noisy'}; |
但是 Spread
只會繼承特性,部會繼承 Function
1 | class C{ |
另外 TypeScript
編譯過程並不允許 generator function
的 Spread
參數傳遞