TypeScript筆記 解構與展開(三)
-
解構
-
解構陣列
let input = [1, 2];
let [first, second] = input;
console.log(first); // outputs 1
console.log(second); // outputs 2
這建立了2個命名變數 first
和 second
。 相當於使用了索引,但更為方便:
first = input[0];
second = input[1];
解構作用於已宣告的變數會更好:
[first, second] = [second, first];
作用於函式引數:
function f([first, second]: [number, number]) { console.log(first); console.log(second); } f(input);
你可以在數組裡使用...
語法建立剩餘變數:
let [first, ...rest] = [1, 2, 3, 4];
console.log(first); // outputs 1
console.log(rest); // outputs [ 2, 3, 4 ]
當然,由於是JavaScript, 你可以忽略你不關心的尾隨元素:
let [first] = [1, 2, 3, 4];
console.log(first); // outputs 1
或其它元素:
let [, second, , fourth] = [1, 2, 3, 4];
-
物件解構
let o = { a: "foo", b: 12, c: "bar" }; let { a, b } = o;
這通過 o.a
and o.b
建立了 a
和 b
。 注意,如果你不需要 c
你可以忽略它。
就像陣列解構,你可以用沒有宣告的賦值:
({ a, b } = { a: "baz", b: 101 });
注意,我們需要用括號將它括起來,因為Javascript通常會將以 {
起始的語句解析為一個塊。
你可以在物件裡使用...
語法建立剩餘變數:
let { a, ...passthrough } = o;
let total = passthrough.b + passthrough.c.length;
-
屬性重新命名
你也可以給屬性以不同的名字:
let { a: newName1, b: newName2 } = o;
這裡的語法開始變得混亂。 你可以將 a: newName1
讀做 "a
作為 newName1
"。 方向是從左到右,好像你寫成了以下樣子:
let newName1 = o.a;
let newName2 = o.b;
令人困惑的是,這裡的冒號不是指示型別的。 如果你想指定它的型別, 仍然需要在其後寫上完整的模式。
let {a, b}: {a: string, b: number} = o;
-
預設值
預設值可以讓你在屬性為 undefined 時使用預設值:
function keepWholeObject(wholeObject: { a: string, b?: number }) {
let { a, b = 1001 } = wholeObject;
}
現在,即使 b
為 undefined , keepWholeObject
函式的變數 wholeObject
的屬性 a
和 b
都會有值。
-
函式宣告
解構也能用於函式宣告。
type C = { a: string, b?: number }
function f({ a, b }: C): void {
// ...
}
但是,通常情況下更多的是指定預設值,解構預設值有些棘手。 首先,你需要在預設值之前設定其格式。
function f({ a="", b=0 } = {}): void {
// ...
}
f();
其次,你需要知道在解構屬性上給予一個預設或可選的屬性用來替換主初始化列表。 要知道 C
的定義有一個 b
可選屬性:
function f({ a, b = 0 } = { a: "" }): void {
// ...
}
f({ a: "yes" }); // ok, default b = 0
f(); // ok, default to {a: ""}, which then defaults b = 0
f({}); // error, 'a' is required if you supply an argument
-
展開
展開操作符正與解構相反。 它允許你將一個數組展開為另一個數組,或將一個物件展開為另一個物件。
let first = [1, 2];
let second = [3, 4];
let bothPlus = [0, ...first, ...second, 5];
這會令bothPlus
的值為[0, 1, 2, 3, 4, 5]
。 展開操作建立了 first
和second
的一份淺拷貝。 它們不會被展開操作所改變。
你還可以展開物件:
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { ...defaults, food: "rich" };
search
的值為{ food: "rich", price: "$$", ambiance: "noisy" }
。 物件的展開比陣列的展開要複雜的多。 像陣列展開一樣,它是從左至右進行處理,但結果仍為物件。 這就意味著出現在展開物件後面的屬性會覆蓋前面的屬性。 因此,如果我們修改上面的例子,在結尾處進行展開的話:
let defaults = { food: "spicy", price: "$$", ambiance: "noisy" };
let search = { food: "rich", ...defaults };
那麼,defaults
裡的food
屬性會重寫food: "rich"
,在這裡這並不是我們想要的結果。
物件展開還有其它一些意想不到的限制。 首先,它僅包含物件 自身的可列舉屬性。 大體上是說當你展開一個物件例項時,你會丟失其方法:
class C {
p = 12;
m() {
}
}
let c = new C();
let clone = { ...c };
clone.p; // ok
clone.m(); // error!