展開語法 (...)

Baseline 已廣泛支援

此功能已成熟,並可在許多裝置和瀏覽器版本上執行。自 2015 年 10 月以來,它已在所有瀏覽器中可用。

擴充套件 (...) 語法允許在預期有零個或多個引數(用於函式呼叫)或元素(用於陣列字面量)的位置展開可迭代物件,例如陣列或字串。在物件字面量中,擴充套件語法列舉物件的屬性,並將鍵值對新增到正在建立的物件中。

擴充套件語法看起來與剩餘語法完全相同。從某種程度上說,擴充套件語法是剩餘語法的反向操作。擴充套件語法將陣列“展開”為它的元素,而剩餘語法則收集多個元素並將其“壓縮”成單個元素。請參閱剩餘引數剩餘屬性

試一試

function sum(x, y, z) {
  return x + y + z;
}

const numbers = [1, 2, 3];

console.log(sum(...numbers));
// Expected output: 6

console.log(sum.apply(null, numbers));
// Expected output: 6

語法

js
myFunction(a, ...iterableObj, b)
[1, ...iterableObj, '4', 'five', 6]
{ ...obj, key: 'value' }

描述

當需要將物件或陣列中的所有元素包含在新陣列或物件中,或者應在函式呼叫的引數列表中逐個應用時,可以使用擴充套件語法。有三個不同的地方接受擴充套件語法:

儘管語法看起來相同,但它們的語義略有不同。

只有可迭代值,如ArrayString,才能在陣列字面量和引數列表中進行擴充套件。許多物件是不可迭代的,包括所有缺少Symbol.iterator方法的普通物件

js
const obj = { key1: "value1" };
const array = [...obj]; // TypeError: obj is not iterable

另一方面,在物件字面量中進行擴充套件會列舉值的自身屬性。對於典型的陣列,所有索引都是可列舉的自身屬性,因此陣列可以擴充套件到物件中。

js
const array = [1, 2, 3];
const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }

所有原始值都可以在物件中展開。只有字串具有可列舉的自身屬性,展開其他任何值都不會在新物件上建立屬性。

js
const obj = { ...true, ..."test", ...10 };
// { '0': 't', '1': 'e', '2': 's', '3': 't' }

當在函式呼叫中使用擴充套件語法時,請注意可能會超出 JavaScript 引擎的引數長度限制。有關更多詳細資訊,請參閱Function.prototype.apply()

示例

函式呼叫中的擴充套件

替換 apply()

在需要將陣列的元素用作函式引數的情況下,通常會使用Function.prototype.apply()

js
function myFunction(x, y, z) {}
const args = [0, 1, 2];
myFunction.apply(null, args);

使用擴充套件語法,上述內容可以寫成:

js
function myFunction(x, y, z) {}
const args = [0, 1, 2];
myFunction(...args);

引數列表中的任何引數都可以使用擴充套件語法,並且擴充套件語法可以使用多次。

js
function myFunction(v, w, x, y, z) {}
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);

應用於 new 運算子

當使用new呼叫建構函式時,無法直接使用陣列和apply(),因為apply()呼叫目標函式而不是構造它,這意味著,除其他事項外,new.target將是undefined。但是,藉助擴充套件語法,陣列可以輕鬆地與new一起使用:

js
const dateFields = [1970, 0, 1]; // 1 Jan 1970
const d = new Date(...dateFields);

陣列字面量中的擴充套件

更強大的陣列字面量

如果沒有擴充套件語法,陣列字面量語法不足以建立一個新陣列,其中包含現有陣列的一部分。相反,必須使用命令式程式碼,結合使用多種方法,包括push()splice()concat()等。有了擴充套件語法,這會變得更加簡潔:

js
const parts = ["shoulders", "knees"];
const lyrics = ["head", ...parts, "and", "toes"];
//  ["head", "shoulders", "knees", "and", "toes"]

就像引數列表的擴充套件一樣,...可以在陣列字面量中的任何位置使用,並且可以使用多次。

複製陣列

您可以使用擴充套件語法對陣列進行淺複製。每個陣列元素都保留其身份而不會被複制。

js
const arr = [1, 2, 3];
const arr2 = [...arr]; // like arr.slice()

arr2.push(4);
// arr2 becomes [1, 2, 3, 4]
// arr remains unaffected

擴充套件語法在複製陣列時實際上只深入一層。因此,它可能不適合複製多維陣列。Object.assign()也是如此——JavaScript 中沒有原生的操作可以進行深克隆。Web API 方法structuredClone()允許深複製某些支援型別的值。有關更多詳細資訊,請參閱淺複製

js
const a = [[1], [2], [3]];
const b = [...a];

b.shift().shift();
// 1

// Oh no! Now array 'a' is affected as well:
console.log(a);
// [[], [2], [3]]

連線陣列的更好方法

Array.prototype.concat()通常用於將陣列連線到現有陣列的末尾。如果沒有擴充套件語法,則按如下方式完成:

js
let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

// Append all items from arr2 onto arr1
arr1 = arr1.concat(arr2);

有了擴充套件語法,這變為:

js
let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

arr1 = [...arr1, ...arr2];
// arr1 is now [0, 1, 2, 3, 4, 5]

Array.prototype.unshift()通常用於在現有陣列的開頭插入一個值陣列。如果沒有擴充套件語法,則按如下方式完成:

js
const arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

//  Prepend all items from arr2 onto arr1
Array.prototype.unshift.apply(arr1, arr2);
console.log(arr1); // [3, 4, 5, 0, 1, 2]

有了擴充套件語法,這變為:

js
let arr1 = [0, 1, 2];
const arr2 = [3, 4, 5];

arr1 = [...arr2, ...arr1];
console.log(arr1); // [3, 4, 5, 0, 1, 2]

注意:unshift()不同,這會建立一個新的arr1,而不是就地修改原始arr1陣列。

有條件地向陣列新增值

您可以使用條件運算子,根據條件使元素在陣列字面量中存在或不存在。

js
const isSummer = false;
const fruits = ["apple", "banana", ...(isSummer ? ["watermelon"] : [])];
// ['apple', 'banana']

當條件為false時,我們展開一個空陣列,這樣最終陣列中就不會新增任何內容。請注意,這與以下情況不同:

js
const fruits = ["apple", "banana", isSummer ? "watermelon" : undefined];
// ['apple', 'banana', undefined]

在這種情況下,當isSummerfalse時,會新增一個額外的undefined元素,並且該元素將被Array.prototype.map()等方法訪問。

物件字面量中的擴充套件

複製和合並物件

您可以使用擴充套件語法將多個物件合併到一個新物件中。

js
const obj1 = { foo: "bar", x: 42 };
const obj2 = { bar: "baz", y: 13 };

const mergedObj = { ...obj1, ...obj2 };
// { foo: "bar", x: 42, bar: "baz", y: 13 }

單個擴充套件會建立原始物件的淺層副本(但不包括不可列舉的屬性,也不復制原型),類似於複製陣列

js
const clonedObj = { ...obj1 };
// { foo: "bar", x: 42 }

覆蓋屬性

當一個物件擴充套件到另一個物件中,或者當多個物件擴充套件到一個物件中,並且遇到具有相同名稱的屬性時,該屬性將採用最後賦給的值,同時保留其最初設定的位置。

js
const obj1 = { foo: "bar", x: 42 };
const obj2 = { foo: "baz", y: 13 };

const mergedObj = { x: 41, ...obj1, ...obj2, y: 9 }; // { x: 42, foo: "baz", y: 9 }

有條件地向物件新增屬性

您可以使用條件運算子,根據條件使元素在物件字面量中存在或不存在。

js
const isSummer = false;
const fruits = {
  apple: 10,
  banana: 5,
  ...(isSummer ? { watermelon: 30 } : {}),
};
// { apple: 10, banana: 5 }

條件為false時的情況是一個空物件,因此沒有任何內容會被擴充套件到最終物件中。請注意,這與以下情況不同:

js
const fruits = {
  apple: 10,
  banana: 5,
  watermelon: isSummer ? 30 : undefined,
};
// { apple: 10, banana: 5, watermelon: undefined }

在這種情況下,watermelon屬性始終存在,並將被Object.keys()等方法訪問。

因為原始值也可以擴充套件到物件中,並且根據所有假值都沒有可列舉屬性的觀察,您可以簡單地使用邏輯與運算子:

js
const isSummer = false;
const fruits = {
  apple: 10,
  banana: 5,
  ...(isSummer && { watermelon: 30 }),
};

在這種情況下,如果isSummer是任何假值,則不會在fruits物件上建立任何屬性。

與 Object.assign() 比較

請注意,Object.assign()可以用於修改物件,而擴充套件語法不能。

js
const obj1 = { foo: "bar", x: 42 };
Object.assign(obj1, { x: 1337 });
console.log(obj1); // { foo: "bar", x: 1337 }

此外,Object.assign()會觸發目標物件上的 setter,而擴充套件語法不會。

js
const objectAssign = Object.assign(
  {
    set foo(val) {
      console.log(val);
    },
  },
  { foo: 1 },
);
// Logs "1"; objectAssign.foo is still the original setter

const spread = {
  set foo(val) {
    console.log(val);
  },
  ...{ foo: 1 },
};
// Nothing is logged; spread.foo is 1

您不能透過一次擴充套件來簡單地重新實現Object.assign()函式:

js
const obj1 = { foo: "bar", x: 42 };
const obj2 = { foo: "baz", y: 13 };
const merge = (...objects) => ({ ...objects });

const mergedObj1 = merge(obj1, obj2);
// { 0: { foo: 'bar', x: 42 }, 1: { foo: 'baz', y: 13 } }

const mergedObj2 = merge({}, obj1, obj2);
// { 0: {}, 1: { foo: 'bar', x: 42 }, 2: { foo: 'baz', y: 13 } }

在上面的示例中,擴充套件語法沒有按預期工作:由於剩餘引數,它將一個引數陣列擴充套件到物件字面量中。下面是使用擴充套件語法的merge實現,其行為類似於Object.assign(),除了它不觸發 setter,也不修改任何物件:

js
const obj1 = { foo: "bar", x: 42 };
const obj2 = { foo: "baz", y: 13 };
const merge = (...objects) =>
  objects.reduce((acc, cur) => ({ ...acc, ...cur }));

const mergedObj = merge(obj1, obj2);
// { foo: 'baz', x: 42, y: 13 }

規範

規範
ECMAScript® 2026 語言規範
# prod-SpreadElement
ECMAScript® 2026 語言規範
# prod-ArgumentList
ECMAScript® 2026 語言規範
# prod-PropertyDefinition

瀏覽器相容性

另見