Оператор розкладу

Оператор розкладу дозволяє розкласти ітерабельний об'єкт, такий як масив чи рядок, там, де очікується нуль чи більше аргументів (для викликів функцій) чи елементів (для масивних літералів), або розкласти об'єкт там, де очікується нуль або більше пар ключ-значення (для об'єктних літералів).

Синтаксис

Для викликів функцій:

myFunction(...iterableObj);

Для масивних літералів або рядків:

[...iterableObj, "4", "п'ять", 6];

Для об'єктних літералів (нове у ECMAScript 2018):

let objClone = { ...obj };

Залишковий синтаксис (параметри)

Залишковий синтаксис виглядає так само, як синтаксис розкладання, але використовується для деструктуризації масивів та об'єктів.

В певному сенсі, залишковий синтаксис є протилежністю синтаксису розкладу. Розклад "розпаковує" масив на його окремі елементи, в той час, як залишковий синтаксис збирає множину елементів та "стискає" їх у єдиний елемент. Дивіться залишкові параметри.

Приклади

Розклад у викликах функцій

Заміна 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]);

Apply для оператора new

Викликаючи конструктор з new, неможливо прямо використовувати масив та apply (apply виконує [[Call]], а не [[Construct]]). Однак, масив можна легко використовувати з new завдяки синтаксису розкладу:

const dateFields = [1970, 0, 1];  // 1 Січня 1970
const d = new Date(...dateFields);

Щоб використати new з масивом параметрів без розкладу, вам довелося б зробити це опосередковано, частковим застосуванням:

function applyAndNew(constructor, args) {
   function partial () {
      return constructor.apply(this, args);
   };
   if (typeof constructor.prototype === "object") {
      partial.prototype = Object.create(constructor.prototype);
   }
   return partial;
}


function myConstructor () {
   console.log("arguments.length: " + arguments.length);
   console.log(arguments);
   this.prop1="знач1";
   this.prop2="знач2";
};

var myArguments = ["вітаю", "як", "ся", "маєте", "п.", null];
var myConstructorWithArguments = applyAndNew(myConstructor, myArguments);

console.log(new myConstructorWithArguments);
// (internal log of myConstructor):           arguments.length: 6
// (internal log of myConstructor):           ["вітаю", "як", "ся", "маєте", "п.", null]
// (log of "new myConstructorWithArguments"): {prop1: "знач1", prop2: "знач2"}

Розклад у масивних літералах

Потужніший масивний літерал

Без оператора розкладу, щоб створити новий масив, частиною якого є існуючий масив, літерального синтаксису масивів недостатньо, доводиться використовувати імперативний код з використанням комбінації з push(), splice(), concat(), і т. д. З синтаксисом розкладу все стає набагато лаконічнішим:

var parts = ['плечі', 'коліна']; 
var lyrics = ['голова', ...parts, 'та', 'пальці']; 
// ["голова", "плечі", "коліна", "та", "пальці"]

Як і розклад аргументів, ... може використовуватись будь-де у масивному літералі, і може використовуватись неодноразово.

Копія масиву

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

arr2.push(4); 
// arr2 стає [1, 2, 3, 4]
// arr залишається незміненим

Заувага: Оператор розкладу ефективно йде на один рівень вглиб при копіюванні масиву. Тому він може бути непридатний для копіювання багатовимірних масивів, як показано у наступному прикладі (те саме з Object.assign() та розкладом).

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

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

//  О, ні!  Тепер масив 'a' також змінився:
a
//  [[], [2], [3]]

Краще об'єднання масивів

Метод Array.prototype.concat() часто використовується для приєднання масиву в кінець існуючого масиву. Без оператора розкладу це робиться так:

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

// Додати всі елементи з arr2 у arr1
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 (недозволене присвоєння)

Метод Array.prototype.unshift() часто використовується для додавання масиву значень у початок існуючого масиву. Без оператора розкладу це робиться так:

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

// Подати усі елементи з arr2 до arr1
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]

Заувага: На відміну від unshift(), цей код створює новий arr1, а не змінює початковий масив arr1.

Розклад у об'єктних літералах

Пропозиція Rest/Spread Properties for ECMAScript (стадія 4) додала розкладені властивості до об'єктних літералів. Цей синтаксис копіює особисті, перелічувані властивості наданого об'єкта у новий об'єкт.

Дрібне клонування (без прототипу) чи злиття об'єктів тепер можливе за допомогою синтаксису, коротшого, ніж Object.assign().

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

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

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

Зауважте, що Object.assign() запускає сетери, а оператор розкладу ні.

Зауважте, що ви не можете ані замінити, ані імітувати функцію Object.assign():

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

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

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

У наведеному прикладі оператор розкладу працює не так, як можна було очікувати: він розкладає масив аргументів у об'єктний літерал, через наявність залишкового параметра.

Тільки для ітерабельних об'єктів

Самі по собі об'єкти не є ітерабельними, але вони стають ітерабельними, коли використовуються у масиві, або з функціями перебору, такими як map()reduce() та assign(). При з'єднанні 2-х об'єктів за допомогою оператору розкладу, вважається, що застосовується функція перебору, коли відбувається з'єднання.

Оператор розкладу (окрім випадку розкладених властивостей) може застосовуватись тільки до ітерабельних об'єктів:

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

Розклад з великою кількістю значень

При використанні оператора розкладу для викликів функцій пам'ятайте про можливість перевищити ліміт кількості аргументів рушія JavaScript. Більше інформації дивіться у статті apply().

Специфікації

Специфікація
ECMAScript (ECMA-262)
The definition of 'Array initializer' in that specification.
ECMAScript (ECMA-262)
The definition of 'Object initializer' in that specification.

Сумісність з веб-переглядачами

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
Spread in array literalsChrome Full support 46Edge Full support 12Firefox Full support 16IE No support NoOpera Full support 37Safari Full support 8WebView Android Full support 46Chrome Android Full support 46Firefox Android Full support 16Opera Android Full support 37Safari iOS Full support 8Samsung Internet Android Full support 5.0nodejs Full support 5.0.0
Full support 5.0.0
Full support 4.0.0
Disabled
Disabled From version 4.0.0: this feature is behind the --harmony runtime flag.
Spread in function callsChrome Full support 46Edge Full support 12Firefox Full support 27IE No support NoOpera Full support 37Safari Full support 8WebView Android Full support 46Chrome Android Full support 46Firefox Android Full support 27Opera Android Full support 37Safari iOS Full support 8Samsung Internet Android Full support 5.0nodejs Full support 5.0.0
Full support 5.0.0
Full support 4.0.0
Disabled
Disabled From version 4.0.0: this feature is behind the --harmony runtime flag.
Spread in destructuringChrome Full support 49Edge Full support 79Firefox Full support 34IE No support NoOpera Full support 37Safari Full support 10WebView Android Full support 49Chrome Android Full support 49Firefox Android Full support 34Opera Android Full support 37Safari iOS Full support 10Samsung Internet Android Full support 5.0nodejs Full support 6.0.0
Spread in object literals
Experimental
Chrome Full support 60Edge Full support 79Firefox Full support 55IE No support NoOpera Full support 47Safari Full support 11.1WebView Android Full support 60Chrome Android Full support 60Firefox Android Full support 55Opera Android Full support 44Safari iOS Full support 11.3Samsung Internet Android Full support 8.2nodejs Full support 8.3.0
Full support 8.3.0
Full support 8.0.0
Disabled
Disabled From version 8.0.0: this feature is behind the --harmony runtime flag.

Legend

Full support  
Full support
No support  
No support
Experimental. Expect behavior to change in the future.
Experimental. Expect behavior to change in the future.
User must explicitly enable this feature.
User must explicitly enable this feature.

Див. також