0%

JS ES6 展開與其餘運算子 Spread and Rest operator 應用

展開與其餘運算子是 ES6 的特性之一,這兩個運算子的寫法都是三個點 … ,這樣的寫法使得在做一些 Array 或 JSON 的處理時,寫法簡潔很多。
透過這篇筆記的整理,希望能更熟悉的應用在自己的實作之中。

  • 展開與其餘運算子的符號是一模一樣的(…)三個點,但情況與意義不同。
  • 中文(…)通常代表其餘更多或忽略,英文 Ellipsis 代表省略,也會用”three dots”與”dot-dot-dot”這三個點
  • 應用在 Array 上,是 ES6 中的其中兩種新特性
  • Spread 展開 : 展開陣列中的值
  • Rest 其餘 : 集合其餘的值成為陣列

展開運算子(spread operator)

摘要

  • 展開運算子可輕易合併多個陣列
  • 可拿來淺拷貝,必免更動到原資料
  • 可以 slice() 來指定取出的陣列元素
  • 也可使用在物件裡並加入資料
  • 可將類陣列轉成一般陣列
  • 可把某個陣列展開,然後傳入函式作為傳入參數值

應用:兩組陣列合併在一起

以往大都使用 concat() 來合併兩個陣列,使用 (…) 可更靈活。

1
2
3
4
5
6
7
let europe = ['Paris', 'Milan', 'Berlin'];
let asia = ['Taipei', 'Tokyo', 'Hongkong'];

const allCity = [...europe, ...asia];
console.log(allCity); // ["paris", "Milan", "Berlin", "Taipei", "Tokyo", "Hongkong"]
const allCityEurope = ['London', ...europe, 'Lyon'];
console.log(allCityEurope); // ['London', 'Paris', 'Milan', 'Berlin', 'Lyon']

陣列展開語法與原本處理的方法比較,使用展開的寫法簡潔許多。

1
2
3
4
5
6
7
8
const values = [1, 2, 3];
const copiedValues = [...values];
// 等同於:
const values = [1, 2, 3];
const copiedValues = [];
for (let i = i; i < values.length; i++) {
copiedValues.push(values[i]);
}

在陣列中以展開加入資料或是,以 slice() 指定要取出的部份

1
2
3
4
5
6
7
8
9
// 前後加資料
const arr = ['a', 'b', 'c'];
const arr2 = ['z', ...arr, 'x'];

console.log(arr); // ['a', 'b', 'c']
console.log(arr2); // ["z", "a", "b", "c", "x"]

// 修改要展開的資料,用slice切片的方式
const arr3 = [...arr.slice(0, 2), 'hi', ...arr.slice(3)]; // ['a', 'b', 'hi']

應用:在物件,以展開加入資料

在物件裡也可以使用展開運算子來加入資料,需注意,如有相同的 key 則會有被覆蓋的風險,並取最後一個。
以下面的範例來說,bar 物件本身就有一個 key a, 在以 Spread operator 的方式將 bar, bar1 加入物件時,bar1, bar2 物件也有兩 key 為 a ,所以重複的會被覆蓋,而以最後一個為主。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const bar = {
a: 1,
b: 2,
c: 3,
};

const bar1 = {
a: 18,
d: 99,
};

const bar2 = {
a: 2, // 會被展開覆蓋掉
...bar,
...bar1,
a: 42, //
};

console.log(bar); // {a: 1, b: 2, c: 3}
console.log(bar2); // {a: 42, b: 2, c: 3, d: 99}

應用:淺層複製(shallow copy)

在操作陣列時,因為陣列為物件型別,以參考傳值的特性,所以複製的陣列如有修改,會改到原本的陣列。
為避免動到原始資料,可利用展開運算子來淺層拷貝。

1
2
3
4
5
6
7
8
9
10
11
12
13
const europe = ['paris', 'Milan', 'Berlin'];
const europe2 = europe;
europe2.push('Lyon');
console.log(europe); // ["paris", "Milan", "Berlin", "Lyon"]
console.log(europe2); // ["paris", "Milan", "Berlin", "Lyon"]

// 淺層拷貝作法
const europe = ['Paris', 'Milan', 'Berlin'];
const europe2 = [...europe];
europe2.push('Lyon');

console.log(europe); // ["Paris", "Milan", "Berlin"]
console.log(europe2); // ['Paris', 'Milan', 'Berlin', 'Lyon']

應用:把類陣列(Array-like)轉成純陣列

類陣列無法繼承陣列的所有方法,類陣列的產生有:從 DOM 抓取而成的陣列、arguments 物件,如果要對類陣列使用部份 Array 方法,需要先轉換成一般陣列。
可利用展開運算子來轉換。

1
2
3
4
const lists = document.querySelectorAll('li');
// 得到的 NodeList 是類陣列,無法使用部分陣列方法
listLiArr = [...lists];
// 轉換成一般陣列

應用:用來把某個陣列展開,然後傳入函式作為傳入參數值,例如加總函式的範例:

1
2
3
4
5
function sum(a, b, c) {
return a + b + c
}
const args = [1, 2, 3]
sum(…args) // 6

其餘運算子 (rest operator)

摘要

  • 其餘運算子 (rest operator)也可稱為其餘參數,也就是傳入的參數。
  • 函式可以使用arguments來取得參數,但是arguments為類陣列,但使用其餘運算子所取得的其餘參數則是陣列。
  • 其餘運算子會收集其餘的(剩餘的)值,將其轉成一個陣列。
  • 會用在函式定義時的傳入參數識別名定義(其餘參數, Rest parameters),以及解構賦值時。
  • 使用情境 1. : 在函式定義中的傳入參數定義中,稱之為其餘參數(Rest parameters)。
  • 使用情境 2. : 用在解構賦值時。

** 把其餘的都放在一起 **

應用:希望取前兩個各自給變數名稱,剩餘的放在另一個變數裡

1
2
3
4
5
const city = ['Taipei', 'Paris', 'Tokyo', 'Lyon', 'Tainan'];
const [capTaiwan, capFrance, ...autre] = city;

console.log(capTaiwan, capFrance); // "Taipei", "Paris"
console.log(autre); // ["Tokyo", "Lyon", "Tainan"]

其餘參數

1
2
3
4
5
6
function sum(a, b, c) {
return a + b + c;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2)); // NaN
console.log(sum(1, 2, 3, 4)); // 6

隨時都有可以加總更多或少於參數數量的數字,遇此狀況可利用其餘參數(…valus)來改寫此函數

1
2
3
4
5
6
7
8
9
function sum(...values) {
let total = 0;
for (let i = 0; i < values.length; i++) {
total += values[i];
}
return total;
}
console.log(sum(1, 2, 3)); // 6
console.log(sum(1, 2, 3, 4, 5));

其餘參數也可以和一般的函數參數使用,要注意一個函式只能有一個其餘參數,且只能寫在參數的最後一個位置

1
2
3
function (x,y, ...values) {
// do something
}

函式在接收參數時

情境:函式不確定會接收幾個參數,可使用其餘運算子的方式,讓參數無論是幾個,都會儲存成一個陣列

1
2
3
4
5
6
7
8
9
10
11
// 加總函式
function addTotal(...nums) {
// 參數成為陣列,也要以陣列的方式處理
let total = 0;
nums.forEach((element) => {
total += element;
});
return total;
}

addTotal(1, 2, 3, 4, 5, 6, 7); // 28

小練習

  1. 將 Paris 放到 europe 裡,其餘放在 asiaCity 裡
1
2
3
4
const city = ['Paris', 'Taipei', 'Tokyo', 'Tainan'];
const [europe, ...asiaCity] = city;
console.log(europe); // 'Paris'
console.log(asiaCity); // ['Taipei', 'Tokyo', 'Tainan']
  1. 把 food 和 drink 合併到 cafeShop 裡,並在中間加一項 beer
1
2
3
4
5
const food = ['cookie', 'cake', 'pizza'];
const drink = ['cafe', 'soda', 'the'];

const cafeShop = [...food, 'beer', ...drink];
console.log(cafeShop); // ['cookie', 'cake', 'pizza', 'beer', 'cafe', 'soda', 'the']

補充: