JavaScript自ES6之後便支援陣列和物件的解構(Destructuring),使我們可以快速地將陣列的元素值和物件中的屬性值指派給變數或是常數來儲存。



直接舉個例子來說明基本的陣列解構和物件解構的語法:

let array = [1, '2'];
let object = {
    f1: 3,
    f2: '4'
};

let [a, b] = array;
let {f1: c, f2: d} = object;

console.log(a);
console.log(b);
console.log(c);
console.log(d);

以上程式的輸出結果如下:

1
2
3
4

將原本應該要宣告常數名稱或是變數名稱的地方,直接改用中括號[]語法來表示陣列結構,或用大括號{}語法來表示物件結構,並在結構中使用自訂的名稱來代替要解構出來的值。我們就不需要寫出如下麻煩的程式啦!

let array = [1, '2'];
let object = {
    f1: 3,
    f2: '4'
};

let a = array[0];
let b = array[1];
let c = object.f1;
let d = object.f2;

console.log(a);
console.log(b);
console.log(c);
console.log(d);

當解構的對象是物件時,我們也可以直接使用屬性名稱來當作變數或常數的名稱。舉例來說:

let {f1: c, f2: d} = {
    f1: 1,
    f2: '2'
};

以上程式可以寫為:

let {f1, f2} = {
    f1: 1,
    f2: '2'
};

如此以來就宣告了f1f2這兩個變數,它們的值分別是數值3和字串4

在TypeScript中,因為會做型別檢查,所以我們在解構物件時,必須要把該型別的所有屬性都寫出來。像是以下程式就會編譯錯誤:

let {f1} = {
    f1: 1,
    f2: '2'
};

這個程式編譯錯誤的原因,其實就和以下程式編譯錯誤的原因是類似的:

let o: {f1: number} = {
    f1: 1,
    f2: '2'
};

應該是TypeScript怕開發者在首次解構大括號物件的型別時,不小心沒寫到物件中有用到的屬性才做的限制。

為了能夠在解構時只取得我們要的物件屬性,要將程式改成以下這樣才能成功編譯:

let o = {
    f1: 1,
    f2: '2'
};

let {f1} = o;

然而解構陣列不會有上述的限制。例如:

let a: [number] = [1, '2'];

以上程式會編譯失敗,但以下程式可以編譯成功:

let [n] = [1, '2'];

巢狀解構

若物件屬性中還有其它物件,而我們想取得物件中的物件的屬性值時,也是可以使用解構語法。例如:

let o = {
    f1: 3,
    f2: '4',
    f3: {
        f4: true
    }
};

let {f3: {f4}} = o;

console.log(f4);

參數解構

函數的參數名稱也是可以套用這樣的解構語法。例如:

let o = {
    f1: 3,
    f2: '4',
    f3: {
        f4: true
    }
};

const isF4True = ({f3: {f4}}: { f3: { f4: boolean } }) => {
    return f4;
};

console.log(isF4True(o));

剩餘屬性解構

還記得在先前的章節中提到的...蔓延語法嗎?我們也可以在解構語法中的最後一個名稱的前方加上...,但這裡就不是原先蔓延語法的功能(將陣列的元素拆成引數)了,而是用來表示所有未被明確寫出來的屬性或元素。

先用陣列來舉例會比較容易一點,如下:

let [n, s, ...remains] = [1, '2', true, Symbol(10)];

console.log(n);
console.log(s);
console.log(remains);

以上程式的輸出結果如下:

1
2
[ true, Symbol(10) ]

let o = {
    f1: 3,
    f2: '4',
    f3: {
        a: 6,
        b: 7,
        c: 8,
    },
    f4: {
        f5: true
    }
};

let {f3: {b, ...remains2}, ...remains} = o;

console.log(b);
console.log(remains);
console.log(remains2);

以上程式的輸出結果如下:

7
{ f1: 3, f2: '4', f4: { f5: true } }
{ a: 6, d: 7 }

解構物件時,...語法只能用在屬性上,不能用在屬性值上。例如以下就是個明顯錯誤的程式:

let o = {
    f1: 3,
    f2: '4',
    f3: false,
};

let {f1: n, f2: ...remains} = o;

陣列的直接解構

陣列並不一定要在宣告名稱時才能被解構,在撰寫一般指派敘述的時候也可以使用解構語法。例如:

let n, s;

[n, s] = [1, '2', false];

console.log(n);
console.log(s);

以上程式會輸出:

1
2

解構時的預設值

有時候我們解構物件出來的屬性值可能會是undefined,如果不想要得到undefined的話,可以利用=替我們宣告出來的名稱加上一個預設值。這個預設值可以是任意型別的值。

例如:

let o: { name: string, age: number, toString: () => string, 'student-number'?: string} = {
    name: 'David',
    age: 18,
    toString: () => `${o.age}: ${o.name}`
};

let {'student-number': studentNumber = 'unknown'} = o;

console.log(studentNumber);

以上程式會輸出:

unknown

陣列解構時也可以設定預設值,例如:

let [n, s, o = {}] = [1, '2'];

總結

在這個章節中,我們學會了如何在TypeScript中,對陣列和物件應用解構語法來簡化程式。在下一個章節要來介紹「try-catch」。

下一章:事件循環(Event Loop)與回呼函數(Callback Function)