JavaScript 之旅 (13):Object Rest/Spread Properties

留言

目錄

  1. Rest Properties
    1. Error
      1. Runtime Error
      2. Static Error
    2. clone 物件
    3. 巢狀物件
    4. 函數擴充額外的 option
    5. 重新組合物件
  2. Spread Properties
    1. clone 物件
    2. 合併多個物件:預設 property 被更新值覆寫 property
    3. 合併 property
  3. 資料來源

本篇介紹 ES2018 (ES9) 提供的 object rest/spread properties。

本文同步發表於 iT 邦幫忙:JavaScript 之旅 (13):Object Rest/Spread Properties

「JavaScript 之旅」系列文章發文於:

在 ES6 時,引入了以下特性:

而此 提案 是在 ES2018 (ES9) 引入了以下特性:

  • 用於物件解構賦值的 rest properties
  • 用於物件字面值的 spread properties

Rest Properties

rest properties 會收集剩餘的 own enumerable property keys,這些 key 都未被解構 pattern 選取。這些 key 和值會複製至新的物件上:

1
2
3
4
5
6
7
8
9
10
11
12
13
let { id, name, ...otherProps } = {
id: 1,
name: 'Titan',
sex: 'Male',
age: 18
};

console.log(id);
// 1
console.log(name);
// Titan
console.log(otherProps);
// {sex: "Male", age: 18}

Error

有些情況下會報錯,例如:

Runtime Error

不能解構賦值 null,否則會出現 TypeError

1
2
3
let data = null;
let { x, y, ...z } = data;
// TypeError: Cannot destructure property 'x' of 'null' as it is null.

解決方法就是給初始值,初始值為空物件:

1
2
3
4
5
6
7
8
9
let data = null ?? {};
let { x, y, ...z } = data;

console.log(x);
// undefined
console.log(y);
// undefined
console.log(z);
// undefined

Static Error

rest property 一定要放在最後一個,否則會出現 SyntaxError

1
2
let { ...x, y, z } = {};
// SyntaxError: Rest element must be last element

並且只能有一個 rest property:

1
2
let { x, ...y, ...z } = {};
// SyntaxError: Rest element must be last element

clone 物件

1
2
3
4
5
6
7
let post = {id: 1, title: 'xxx'};
let { ...clonedPost } = post;

console.log(clonedPost);
// {id: 1, title: "xxx"}
console.log(post === clonedPost);
// false

但不包含 prototype,只會複製 own enumerable property:

1
2
3
4
5
6
7
8
let post = {id: 1, title: 'xxx'};
let extendedPost = Object.create(post);
extendedPost.likeCount = 666;
extendedPost.shareCount = 99;

let { ...clonedPost } = extendedPost;
console.log(clonedPost);
// {likeCount: 666, shareCount: 99}

巢狀物件

1
2
3
4
5
6
7
8
9
10
11
12
let post = {
emoji: { like: 1, love: 2, wow: 3, haha: 4 },
};

let {
emoji: { like: likeCount, ...otherEmojis },
} = post;

console.log(likeCount);
// 1
console.log(otherEmojis);
// {love: 2, wow: 3, haha: 4}

函數擴充額外的 option

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function baseFunc({ a, b }) {
console.log(`a: ${a}`);
console.log(`b: ${b}`);
}
function wrapperFunc({ x, y, ...restConfig }) {
console.log(`x: ${x}`);
console.log(`y: ${y}`);
console.log(restConfig);
return baseFunc(restConfig);
}

wrapperFunc({a: 1, b: 2, x: 4, y: 5});
// x: 4
// y: 5
// {a: 1, b: 2}
// a: 1
// b: 2

重新組合物件

1
2
3
4
5
6
let assembled = { x: 1, y: 2, a: 3, b: 4 };
let { x, y, ...z } = assembled;
let reassembled = { x, ...z };

console.log(reassembled);
// {x: 1, a: 3, b: 4}

Spread Properties

物件 initializer 中的 spread properties 會將 own enumerable properties 從提供的物件複製到新建立的物件:

1
2
3
4
5
6
7
8
9
10
let id = 1;
let name = 'Titan';
let otherProps = {
sex: 'Male',
age: 18
}

let person = { id, name, ...otherProps };
console.log(person);
// {id: 1, name: "Titan", sex: "Male", age: 18}

若值為 nullundefined 會被忽略:

1
2
3
let emptyObject = { ...null, ...undefined };
console.log(emptyObject);
// {}

clone 物件

1
2
3
4
5
6
7
let post = {id: 1, title: 'xxx'};
let clonedPost = {...post};

console.log(clonedPost);
// {id: 1, title: "xxx"}
console.log(post === clonedPost);
// false

過去可能會用 Object.assign()

1
2
3
4
5
6
7
let post = {id: 1, title: 'xxx'};
let clonedPost = Object.assign({}, post);

console.log(clonedPost);
// {id: 1, title: "xxx"}
console.log(post === clonedPost);
// false

合併多個物件:預設 property 被更新值覆寫 property

1
2
3
4
5
6
let initUser = {name: '', age: null};
let updateUser = {name: 'Titan'};
let user = {...initUser, ...updateUser};

console.log(user);
// {name: "Titan", age: null}

過去可能會用 Object.assign()

1
2
3
4
5
6
let initUser = {name: '', age: null};
let updateUser = {name: 'Titan'};
let user = Object.assign({}, initUser, updateUser);

console.log(user);
// {name: "Titan", age: null}

合併 property

1
2
3
4
5
let date = {from: '2020-01-01', to: '2020-06-01'};
let activity = {id: 1, ...date};

console.log(activity);
// {id: 1, from: "2020-01-01", to: "2020-06-01"}

過去可能會用 Object.assign()

1
2
3
4
5
6
let date = {from: '2020-01-01', to: '2020-06-01'};
let id = 1;
let activity = Object.assign({}, {id}, date);

console.log(activity);
// {id: 1, from: "2020-01-01", to: "2020-06-01"}

資料來源

分享:

討論區