Blog
Blog copied to clipboard
[MDN] Spread 語法
Spread 語法
- 將
陣列
,字串
(iterable 類型) 展開, 就會有 0 到多個-
參數
(用於函式呼叫時) -
項目
(用於 array literal 時)
-
- 將
物件
展開, 就會有 0 到多個-
key-value
配對(用於 object literal 時).
-
說明
- 當你需要將物件或陣列的所有項目以某種形式的列表呈現時, 使用他.
function sum(x, y, z) {
return x + y + z;
}
const numbers = [1, 2, 3];
console.log(sum(...numbers));
// 預期輸出: 6
console.log(sum.apply(null, numbers));
// 預期輸出: 6
function sum(x, y, z, n) {
return x + y + z + n;
}
語法
- 用於函式呼叫:
myFunction(...iterableObj); // 傳入 iterableObj 所有的項目
- 用於 array literal 或字串:
[...iterableObj, "4", "five", 6]; // 加入 iterableObj 的所有項目
- 用於 object literal
let objClone = { ...obj }; // 傳入物件所有 key:value 配對
Rest 語法(參數)
- rest 語法實際上就像 spread 一樣. 但彼此是相反的. spread 是 "展開" 陣列的項目, 而 rest 是 "壓縮" 多個項目.
範例
用於函式呼叫
取代 apply()
- 過去如果我想將陣列的項目傳入到函式,
Function.prototype.apply()
是常見用法.
function myFunction(x, y, z) {}
let args = [0, 1, 2];
// myFunction.apply(null, args);
myFunction(...args);
- 可以用於任何參數, 而且可以多次使用.
function myFunction(v, w, x, y, z) {}
let args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
用於 new
- 過去當要在
new Constructor(...)
將陣列的項目傳入, 沒辦法直接用apply()
(對象是[[Call]]
, 並不是[[Construct]]
).
let dateFields = [1970, 0, 1]; // 1 Jan 1970
let d = new Date(...dateFields);
function Car(speed = "fast", size = "big") {
console.log("參數長度: " + arguments.length);
console.log(arguments);
this.speed = speed;
this.size = size;
}
// const car = new Car("slow", "small");
// 參數長度: 2
// ["slow", "small"]
const carArgs = ["slow", "small", "red"];
function applyAndNew(constructor, args) {
function partial() {
return constructor.apply(this, args);
}
if (typeof constructor.prototype === "object") {
partial.prototype = Object.create(constructor.prototype);
}
return partial;
}
const CarWithArguments = applyAndNew(Car, carArgs);
console.log(new CarWithArguments());
// (Car): 參數長度: 3
// (Car): ["slow", "small", "red"]
// (new CarWithArguments): {speed: "slow", size: "small"}
用於 array literal
更強大的 array literal
- 過去要用現存的陣列建立一個新的陣列, 必須使用
push()
,splice()
,concat()
等.
let parts = ["shoulders", "knees"];
let lyrics = ["head", ...parts, "and", "toes"];
// ["head", "shoulders", "knees", "and", "toes"]
陣列複製
let arr = [1, 2, 3];
let arr2 = [...arr]; // 像是 arr.slice()
arr2.push(4);
// arr2 變成 [1, 2, 3, 4]
// arr 剩餘的項目不受影響
注意: 陣列複製, 只會複製一層深. 所以不適合用於複製多維陣列.
let a = [[1], [2], [3]];
let b = [...a];
b.shift().shift();
// 1
// 喔不! 現在陣列 'a' 也被影響:
a;
// [[], [2], [3]]
concat 陣列
- 用
Array.prototype.concat()
將陣列接續連到另一個現存陣列:
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = arr1.concat(arr2);
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2];
// arr1 現在是 [0, 1, 2, 3, 4, 5]
// 注意: 不要使用 const 否則會給 TypeError (invalid assignment)
- 用
Array.prototype.unshift()
將陣列塞入到另一個現存陣列的開頭:
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
Array.prototype.unshift.apply(arr1, arr2);
// arr1 現在是 [3, 4, 5, 0, 1, 2]
let arr1 = [0, 1, 2];
let arr2 = [3, 4, 5];
arr1 = [...arr2, ...arr1];
// arr1 現在是 [3, 4, 5, 0, 1, 2]
注意: 他會建立新的
arr1
, 並不會修改原本的arr1
, 與unshift()
不同.
用於 object literal
- 他會從提供的物件複製屬性到新的物件.
- Shallow-cloning(淺複製, 不包含 prototype) 或 物件合併 都可以使用, 他比
Object.assign()
更短的語法.
let obj1 = { foo: "bar", x: 42 };
let obj2 = { foo: "baz", y: 13 };
let clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }
let mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }
- 注意
Object.assign()
會觸發setters
, 而 spread 語法不會. - 注意, 你無法取代或仿造
Object.assign
函式:
let obj1 = { foo: "bar", x: 42 };
let obj2 = { foo: "baz", y: 13 };
const merge = (...objects) => ({ ...objects });
let mergedObj1 = merge(obj1, obj2);
// Object { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }
let mergedObj2 = merge({}, obj1, obj2);
// Object { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }
只用於 iterable
- 物件本身非 iterable, 但是當用在陣列, 或 iterable 函式(像
map
,reduce
和assign
), 他們就會變成 iterable.
let obj = { key1: "value1" };
let array = [...obj]; // TypeError: obj 不可迭代
用於多值
- 當用於函式呼叫, 注意可能超過 JavaScript 引擎所限制的參數長度.
瀏覽器相容性
- 只有 IE 不支援, 其他瀏覽器都完全支援