JavaScript 「...」 Spread Operator 和 Rest Operator 一次搞懂差異
...
當 Spread Operator
用
我的 mental model 中,會把 Spread Operator 想像成:把外框 {}
[]
,把裡面的 property 或元素一個一個拿出來,顧名思義就是把包在裡面的東西「展開」
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
// ...arr1 如同分別拿出 1,2,3
// ...arr2 如圖分別拿出 4,5,6
// [...arr1, ...arr2] 就是 [1,2,3,4,5,6]
合併陣列、物件
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
let newArr = [...arr1, ...arr2];
console.log(newArr);
// [ 1, 2, 3, 4, 5, 6 ]
let obj1 = { name: "Alex", job: "F2E" };
let obj2 = { name: "Alexis", habits: "reading" };
let newObj = { ...obj1, ...obj2 };
console.log(newObj);
// { name: 'Alexis', job: 'F2E', habits: 'reading' }
// 重複 property 會以最後一個為準
複製陣列、物件(淺拷貝)
let orgArr = [1, 2, 3];
// 複製陣列
let newArr = [...orgArr];
newArr.push(4);
console.log(orgArr);
// [1,2,3]
console.log(newArr);
// [1,2,3,4]
// 原陣列不受影響
// 物件中包物件
let data = { job: "F2E" };
let orgObj = { name: "Alexis", data: data };
// 複製物件
let newObj = { ...orgObj };
newObj.age = 18;
// 新增純值屬性, orgObj 不受影響
newObj.data.location = "KH";
// data 是物件, 新舊物件都會指向同個記憶體, 修改會一起受影響
console.log(orgObj);
// { name: 'Alexis', data: { job: 'F2E', location: 'KH' } }
console.log(newObj);
// { name: 'Alexis', data: { job: 'F2E', location: 'KH' }, age: 18 }
函數呼叫
function add(a, b, c) {
return a + b + c;
}
let arr = [1, 2, 3];
console.log(add(...arr)); // 6
// add(...arr) 等同於
// add(1,2,3)
把 Iterable / Array-like 轉為純陣列
- 可迭代物件 : String、Array、TypedArray、Map、Set
- 偽陣列物件 : 函式的「arguments」、DOM 的「NodeList」
let str = "Hello, world!";
console.log([...str]);
// [ 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!' ]
...
當 Rest Operator
用
顧名思義,就把 剩下的東西收集起來,打成一包
其餘參數
以下列程式碼為例,第一個傳入的參數是 name
,之後 傳進來的參數都把他們打包成矩陣 numbers
function sum(name, ...numbers) {
let result = numbers.reduce((acc, cv) => acc + cv);
console.log(name, result);
}
sum("arr", 1, 2, 3, 4, 5, 6); // arr 21
// 函式內 numbers = [1, 2, 3, 4, 5, 6]
sum("arr", 2, 3); // 5
// 函式內 number = [2, 3]
當然只放其餘參數也 ok
function pureSum(...numbers) {
let result = numbers.reduce((acc, cv) => acc + cv);
console.log(result);
}
pureSum(1, 2, 3, 4, 5, 6); // 21
pureSum(2, 3); // 5
但是要注意 其餘參數一定要放在最後一個(精確來說,其餘參數後面不能再接 ,
),如 function(a, b, c, ...rest)
,而不能 function(a, b, ...rest, c)
可能會覺得 ...
好像都可以用在函數呼叫,好容易搞混怎麼辦?來做個整理
- 展開 用於函數
- 函數呼叫 時使用
- 把 arr 展開,以適用於多參數的函數
- 其餘 用於函數
- 函數宣告時 使用
- 把參數收集打包,變成一個陣列,在函數內部使用
- 現在會建議使用其餘參數風格,而非
arguments
解構賦值
解構賦值的時候,把解構出來後剩餘的東西打包(原來的陣列或物件變數不會受影響)
const [x, ...y] = [1, 2, 3];
console.log(x); // 1
// 把第一個元素取出來
console.log(y); // [2,3]
// 其餘打包到 y
const obj = { name: "Alex", age: 18, location: "KH", job: "F2E" };
// 一次示範 解構, 解構別名, 其餘運算子
const { name, job: JOB, ...restProp } = obj;
console.log(name, JOB);
// Alex F2E
console.log(restProp);
// { age: 18, location: 'KH' }
console.log(obj);
// { name: 'Alex', age: 18, location: 'KH', job: 'F2E' }
// 原物件解構後不受影響