Blog icon indicating copy to clipboard operation
Blog copied to clipboard

[MDN] Spread 語法

Open ChaoLiou opened this issue 4 years ago • 0 comments

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, reduceassign), 他們就會變成 iterable.
let obj = { key1: "value1" };
let array = [...obj]; // TypeError: obj 不可迭代

用於多值

  • 當用於函式呼叫, 注意可能超過 JavaScript 引擎所限制的參數長度.

瀏覽器相容性

  • 只有 IE 不支援, 其他瀏覽器都完全支援

ChaoLiou avatar Jan 22 '21 08:01 ChaoLiou