Синтаксис расширения (...)
Базовый уровень Широко доступно
Эта функция хорошо зарекомендовала себя и работает во многих устройствах и версиях браузеров. Она доступна во всех браузерах с января 2020 года.
Синтаксис расширения (...) позволяет итерируемому объекту, такому как массив или строка, быть развёрнутым в местах, где ожидается ноль или более аргументов (для вызовов функций) или элементов (для литералов массивов). В литерале объекта синтаксис расширения перечисляет свойства объекта и добавляет пары ключ-значение к создаваемому объекту.
Синтаксис расширения выглядит точно так же, как синтаксис оставшихся параметров. В некотором смысле синтаксис расширения противоположен синтаксису оставшихся параметров. Синтаксис расширения "разворачивает" массив в его элементы, в то время как синтаксис оставшихся параметров собирает несколько элементов и "сжимает" их в один элемент. Смотрите оставшиеся параметры и свойство оставшихся параметров.
Попробуйте
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
Синтаксис
myFunction(a, ...iterableObj, b)
[1, ...iterableObj, '4', 'five', 6]
{ ...obj, key: 'value' }
Описание
Синтаксис расширения может использоваться, когда все элементы объекта или массива должны быть включены в новый массив или объект, либо должны быть применены по отдельности в списке аргументов вызова функции. Существуют три различных места, где принимается синтаксис расширения:
-
Список аргументов функции (
myFunction(a, ...iterableObj, b)) -
Литералы массивов (
[1, ...iterableObj, '4', 'five', 6]) -
Литералы объектов (
{ ...obj, key: 'value' })
Хотя синтаксис выглядит одинаково, они обладают немного разной семантикой.
Только итерируемые значения, такие как Array и String, могут быть расширены в литералах массивов и списках аргументов. Многие объекты не являются итерируемыми, включая все простые объекты, которым не хватает метода Symbol.iterator:
const obj = { key1: "value1" };
const array = [...obj]; // TypeError: obj is not iterable
С другой стороны, расширение в литералах объектов перечисляет собственные свойства значения. Для типичных массивов все индексы являются перечисляемыми собственными свойствами, поэтому массивы могут быть расширены в объекты.
const array = [1, 2, 3];
const obj = { ...array }; // { 0: 1, 1: 2, 2: 3 }
Все примитивы могут быть расширены в объекты. Только строки имеют перечисляемые собственные свойства, и расширение чего-либо другого не создает свойств в новом объекте.
const obj = { ...true, ..."test", ...10 };
// { '0': 't', '1': 'e', '2': 's', '3': 't' }
При использовании синтаксиса расширения для вызовов функций следует помнить о возможности превышения лимита длины аргументов движка JavaScript. Для получения дополнительной информации см. Function.prototype.apply().
Примеры
Расширение в вызовах функций
Замена apply()
Обычно Function.prototype.apply() используется в тех случаях, когда требуется использовать элементы массива в качестве аргументов для функции.
function myFunction(x, y, z) {}
const args = [0, 1, 2];
myFunction.apply(null, args);
С синтаксисом расширения вышеприведенное можно записать так:
function myFunction(x, y, z) {}
const args = [0, 1, 2];
myFunction(...args);
Любой аргумент в списке аргументов может использовать синтаксис расширения, и синтаксис расширения может использоваться несколько раз.
function myFunction(v, w, x, y, z) {}
const args = [0, 1];
myFunction(-1, ...args, 2, ...[3]);
Применение для оператора new
При вызове конструктора с new, невозможно напрямую использовать массив и apply(), потому что apply() вызывает целевую функцию вместо конструирования, что, среди прочего, означает, что new.target будет undefined. Однако массив можно легко использовать с new благодаря синтаксису расширения:
const dateFields = [1970, 0, 1]; // 1 Jan 1970 const d = new Date(...dateFields);
Расширение в литералах массивов
Более мощный литерал массива
Без синтаксиса расширения синтаксис литерала массива больше не достаточен для создания нового массива, используя существующий массив как его часть. Вместо этого должен использоваться императивный код с применением комбинации методов, включая push(), splice(), concat() и т. д. С синтаксисом расширения это становится гораздо более лаконичным:
const parts = ["shoulders", "knees"]; const lyrics = ["head", ...parts, "and", "toes"]; // ["head", "shoulders", "knees", "and", "toes"]
Так же как и расширение для списков аргументов, ... может использоваться в любом месте литерала массива и может применяться более одного раза.
Копирование массива
Вы можете использовать синтаксис расширения для создания поверхностной копии массива. Каждый элемент массива сохраняет свою идентичность, не будучи скопированным.
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 не выполняет глубокого клонирования. Метод веб-API structuredClone() позволяет глубоко копировать значения определенных поддерживаемых типов. Дополнительные сведения см. в разделе поверхностное копирование.
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() часто используется для конкатенации массива в конец существующего массива. Без синтаксиса расширения это делается так:
let arr1 = [0, 1, 2]; const arr2 = [3, 4, 5]; // Append all items from arr2 onto arr1 arr1 = arr1.concat(arr2);
С синтаксисом расширения это выглядит так:
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() часто используется для вставки массива значений в начало существующего массива. Без синтаксиса расширения это делается так:
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]
С синтаксисом расширения это выглядит так:
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 на месте.
Условное добавление значений в массив
Вы можете сделать элемент присутствующим или отсутствующим в литерале массива, в зависимости от условия, используя условный оператор.
const isSummer = false; const fruits = ["apple", "banana", ...(isSummer ? ["watermelon"] : [])]; // ['apple', 'banana']
Когда условие равно false, мы расширяем пустой массив, чтобы ничего не добавлялось в конечный массив. Обратите внимание, что это отличается от следующего:
const fruits = ["apple", "banana", isSummer ? "watermelon" : undefined]; // ['apple', 'banana', undefined]
В этом случае добавляется дополнительный элемент undefined, когда isSummer является false, и этот элемент будет посещен такими методами, как Array.prototype.map().
Расширение в литералах объектов
Копирование и слияние объектов
Вы можете использовать синтаксис расширения для слияния нескольких объектов в один новый объект.
const obj1 = { foo: "bar", x: 42 };
const obj2 = { bar: "baz", y: 13 };
const mergedObj = { ...obj1, ...obj2 };
// { foo: "bar", x: 42, bar: "baz", y: 13 }
Однократное расширение создает поверхностную копию исходного объекта (но без неперечисляемых свойств и без копирования прототипа), подобно копированию массива.
const clonedObj = { ...obj1 };
// { foo: "bar", x: 42 }
Переопределение свойств
Когда один объект расширяется в другой объект, или когда несколько объектов расширяются в один объект, и встречаются свойства с одинаковыми именами, свойство принимает последнее присвоенное значение, оставаясь при этом в той позиции, в которой оно было первоначально установлено.
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 }
Условное добавление свойств в объект
Вы можете сделать элемент присутствующим или отсутствующим в литерале объекта, в зависимости от условия, используя условный оператор.
const isSummer = false;
const fruits = {
apple: 10,
banana: 5,
...(isSummer ? { watermelon: 30 } : {}),
};
// { apple: 10, banana: 5 }
Случай, когда условие равно false, представляет собой пустой объект, так что ничего не расширяется в конечный объект. Обратите внимание, что это отличается от следующего:
const fruits = {
apple: 10,
banana: 5,
watermelon: isSummer ? 30 : undefined,
};
// { apple: 10, banana: 5, watermelon: undefined }
В этом случае свойство watermelon всегда присутствует и будет посещено такими методами, как Object.keys().
Поскольку примитивы также могут быть расширены в объекты, и исходя из того, что все ложные значения не имеют перечисляемых свойств, вы можете просто использовать оператор логическое И:
const isSummer = false;
const fruits = {
apple: 10,
banana: 5,
...(isSummer && { watermelon: 30 }),
};
В этом случае, если isSummer является любым ложным значением, никакое свойство не будет создано в объекте fruits.
Сравнение с Object.assign()
Обратите внимание, что Object.assign() может использоваться для изменения объекта, в то время как синтаксис расширения — нет.
const obj1 = { foo: "bar", x: 42 };
Object.assign(obj1, { x: 1337 });
console.log(obj1); // { foo: "bar", x: 1337 }
Кроме того, Object.assign() вызывает сеттеры на целевом объекте, в то время как синтаксис расширения — нет.
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() через однократное расширение:
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(), за исключением того, что он не вызывает сеттеры и не изменяет ни один объект:
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® 2027 # prod-SpreadElement |
| Спецификация языка ECMAScript® 2027 # prod-ArgumentList |
| Спецификация языка ECMAScript® 2027 # prod-PropertyDefinition |
Совместимость с браузерами
| Desktop | Mobile | Server | |||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Chrome | Edge | Firefox | Opera | Safari | Chrome Android | Firefox for Android | Opera Android | Safari on iOS | Samsung Internet | WebView Android | WebView on iOS | Bun | Deno | Node.js | |
spread |
46 |
12 |
16 |
37 |
8 |
46 |
16 |
37 |
8 |
5.0 |
46 |
8 |
1.0.0 |
1.0 |
5.0.0 |
spread_in_arrays |
46 |
12 |
16 |
37 |
8 |
46 |
16 |
37 |
8 |
5.0 |
46 |
8 |
1.0.0 |
1.0 |
5.0.0 |
spread_in_function_calls |
46 |
12 |
27 |
37 |
8 |
46 |
27 |
37 |
8 |
5.0 |
46 |
8 |
1.0.0 |
1.0 |
5.0.0 |
spread_in_object_literals |
60 |
79 |
55 |
47 |
11.1 |
60 |
55 |
44 |
11.3 |
8.2 |
60 |
11.3 |
1.0.0 |
1.0 |
8.3.0 |
См. также
© 2005–2025 MDN contributors.
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax