[JavaScript] Javascript ES6 中的展開/其餘運算子(Spread operator/Rest operator)

itsems
itsems_frontend
Published in
8 min readAug 23, 2020
Photo by Victoria Strukovskaya on Unsplash

展開/其餘運算子(Spread operator/Rest operator) 都是 Javascript ES6 中的特性,兩者的符號雖然都是 ,但是在使用上略有差異。

Outline:
+ 展開運算子(Spread operator)
+ 其餘運算子(Rest operator)
+ 總結與差異 Summary&Differences

展開運算子(Spread operator)

又稱展開運算符、Spread syntax,會將陣列展開成個別值灑進去。

常見用途 1 — 似 cancat 用法

在 spread operator 出現之前,如果我們要結合兩個陣列,concat 會是一種常見的方式:

var arr1 = ['emma','is'];
var arr2 = [18,'years old'];
var combinedArr = arr1.concat(arr2);console.log(combinedArr); // ["emma", "is", 18, "years old"]

透過 spread operator,就可以簡略為:

var arr1 = ['emma','is'];
var arr2 = [...arr1, 18,'years old'];
console.log(arr2); // ["emma", "is", 18, "years old"]

或多個 spread operator 放在一起:

var arr1 = ['emma','is'];
var arr2 = [18,'years old'];
var combinedArr = [...arr1, ...arr2];console.log(combinedArr); // ["emma", "is", 18, "years old"]

也可以做為陣列的淺拷貝(不會影響到被拷貝的陣列):

var arr1 = ['emma','is'];
var arr2 = [...arr1];
arr2.push(18, 'years old'); // 不會影響到 arr1
console.log(arr2); // ["emma", "is", 18, "years old"]
console.log(arr1); // ['emma', 'is']

常見用途 2 — 傳入函式作為參數

在 ES6 之前,如果要將陣列展開傳入函式,會用上 apply 這個方法,他的第二個參數是陣列,apply 的詳細說明可以看 w3c

function mySum(x,y){
return x+y;
}
var arr = [1,2];
mySum.apply(null, arr) // 3

使用 spread operator 則方便許多:

function mySum(x, y){
return x+y;
}
var arr = [1,2];
Sum(...arr); // 3

常見用途 3 — 將可迭代 (literable) 的物件轉為陣列

Javascript 中可迭代的物件有 String, Array, TypedArray, Map, Set 物件:

const name = 'emma';
const spreadName = [...name];
console.log(spreadName) // ['e','m','m','a']

其餘運算子(Rest operator)

又稱為其餘運算符,可以將剩下的值集合成一個陣列。

常見用途 1 — 其餘參數 (Rest parameters)

用於想要傳入一個不確定數量的值給函式作為參數:
須注意在傳入函式時,必須是參數中的最後一位,而且參數中只能有一個其餘參數

// 回傳參數加總
function sumUp(...nums){
console.log(nums); // 傳入的參數被組成陣列
let total = 0;
nums.forEach((number)=>{
total += number; // 將陣列內的數字加總起來
})
return total; // 回傳 total
}
sumUp(1) // [1], 1
sumUp(1,2,3) // [1,2,3], 6

傳入多個參數:

function many(x,y, ...z){ // 指定了兩個參數和一個剩餘參數
console.log('x:',x); // 印出 x
console.log('y:',y); // 印出 y
console.log('z:',z); // 印出剩餘參數 z
}
many('emma', 'is', 18, 'years', 'old');
// x: emma
// y: is
// z: [18,'years','old'] // 後面剩下的被組成一個陣列了

即使只有傳入一個值,也會被組成陣列:

many('emma', 'is', 'good')
// x: emma
// y: is
// z: ['good']

如果沒有傳入值,就會成為一個空的陣列,而不是 undefined

many('emma', 'is')
// x: emma
// y: is
// z: []

常見用途 2 — 解構賦值 (destructuring)

解構賦值可以想像成鏡像的方式來進行賦值,

解構陣列:

const [a,b] = [1,2];
console.log(a); // 1
console.log(b); // 2

使用其餘運算子解構陣列:

const [a, ...b] = [1, 2, 3];
console.log(a); // 1
console.log(b); // [2, 3]

解構物件:

let me = {
name: 'emma',
age: 18
};
let { name, age } = me;
console.log(name); // emma
console.log(age); // 18

使用其餘運算子解構物件 :

let {a, b, ...rest} = { a:1, b:2, c:3, d:4 };
console.log(a); // 1
console.log(b); // 2
console.log(rest); // {c:3, d:4}

如果和其餘參數出現一樣的狀況,數量不相等時,也會成為空的陣列:

let [c, ...d] = [1]
console.log(c); // 1
console.log(d); // []

總結與差異 Summary&Differences

展開運算子的概念可以想成是一種灑進去的感覺,把陣列或是可迭代的物件展開成一個一個獨立的值,再灑進使用他的地方。而其餘運算子則是集合剩下來的值組合成陣列,讓我們可以傳遞未知數量的參數至函式中

📝 Quiz time !

以下取自 JavaScript Questions 中有關 hoisting 的小測驗,希望看完上面的說明,下面的題目可以讓你迎刃而解:

What does this return?

[...'Lydia'];
  • A: ["L", "y", "d", "i", "a"]
  • B: ["Lydia"]
  • C: [[], "Lydia"]
  • D: [["L", "y", "d", "i", "a"]]
Photo by Caleb Woods on Unsplash

Answer: A

這裡是展開運算子的做法,會將可迭代物件撒開

What’s the output?

const user = { name: 'Lydia', age: 21 };
const admin = { admin: true, ...user };
console.log(admin);
  • A: { admin: true, user: { name: "Lydia", age: 21 } }
  • B: { admin: true, name: "Lydia", age: 21 }
  • C: { admin: true, user: ["Lydia", 21] }
  • D: { admin: true }
Photo by Caleb Woods on Unsplash

Answer: B

這裡也是展開運算子的作法,可以將兩個物件結合, user 物件裡面單個 key/value 被拆開來灑進 admin 物件中。

內容若有任何錯誤,歡迎留言交流指教! 🦕

ref:
JavaScript ES6: Spread Operator and Rest Parameters
展開運算符與其餘運算符
MDN-Spread syntax (…)
MDN-Rest parameters

--

--

itsems
itsems_frontend

Stay Close to Anything that Makes You Glad You are Alive.