Оператор розпакування

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

Синтаксис

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

myFunction(...iterableObj);

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

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

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

let objClone = { ...obj };

Приклади

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

Заміна apply()

Зазвичай, Function.prototype.apply() використовується, коли ви бажаєте використати елементи масиву як аргументи у функції.

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

З оператором розпакування наведене можна записати так:

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

Будь-який аргумент у списку може використати розпакування, і воно може використовуватись неодноразово.

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

Apply для new

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

var dateFields = [1970, 0, 1];  // 1 Січня 1970
var 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, 'та', 'пальці']; 
// ["голова", "плечі", "коліна", "та", "пальці"]

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

Копія масиву

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

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

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

var a = [[1], [2], [3]];
var b = [...a];
b.shift().shift(); // 1
// Тепер масив a також змінено: [[], [2], [3]]

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

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

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Додати всі елементи з arr2 у arr1
arr1 = arr1.concat(arr2);

З оператором розпакування отримуємо:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1 = [...arr1, ...arr2]; // arr1 тепер [0, 1, 2, 3, 4, 5]

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

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Подати усі елементи з arr2 до arr1
Array.prototype.unshift.apply(arr1, arr2) // arr1 тепер [3, 4, 5, 0, 1, 2]

З оператором розпакування отримуємо:

var arr1 = [0, 1, 2];
var 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().

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

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

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

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

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

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

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

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

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

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

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

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

Розпакування великої кількості значень

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

Залишкові параметри

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

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

Специфікація Статус Коментар
ECMAScript 2015 (6th Edition, ECMA-262) Standard Визначені у декількох розділах специфікації: Ініціалізатор масиву, Argument Lists
ECMAScript 2018 (ECMA-262) Standard Визначено у розділі Ініціалізатор об'єкта
ECMAScript Latest Draft (ECMA-262) Draft Без змін.
ECMAScript Latest Draft (ECMA-262) Draft Без змін.

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

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 No support NoFirefox Full support 34IE No support NoOpera Full support 37Safari ? WebView Android Full support 49Chrome Android Full support 49Firefox Android Full support 34Opera Android Full support 37Safari iOS ? Samsung Internet Android Full support 5.0nodejs Full support Yes
Spread in object literals
Experimental
Chrome Full support 60Edge No support NoFirefox 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 ? Safari 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
Compatibility unknown  
Compatibility unknown
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.

Див. також