Składnia rozwinięcia

Składnia rozwinięcia (ang. spread syntax) pozwala na rozwinięcie iterowalnego wyrażenia, takiego jak wyrażenie tablicowe lub ciąg znaków, tam gdzie oczekiwanych jest zero lub więcej argumentów (dla wywołań funkcji) lub elementów (dla literałów tablicowych). Pozwala również na rozwinięcie wyrażeń obiektowych w miejscach, gdzie oczekiwanych jest zero lub więcej par klucz-wartość (dla literałów obiektowych).
 

Składnia

Dla wywołań funkcji:

mojaFunkcja(...iterowalnyObiekt);

Dla literałów tablicowych lub łańcuchów znaków:

[...iterowalnyObiekt, '3', 'cztery', 5];

Dla literałów obiektowych (nowe w ECMAScript 2018):

let klonObiektu = { ...obiekt };

Przykłady

Rozwinięcie w wywołaniach funkcji

Zastąpienie apply

Powszechne jest używanie Function.prototype.apply w przypadkach, w których chcemy użyć elementów tablicy jako argumentów funkcji.

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

Przy użyciu operatora rozpakowania można to zapisać jako:

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

Każdy argument na liście argumentów może użyć operatora rozpakowania, można go także używać wielokrotnie.

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

Apply for new

When calling a constructor with new, it's not possible to directly use an array and apply (apply does a [[Call]] and not a [[Construct]]). However, an array can be easily used with new thanks to spread syntax:

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

To use new with an array of parameters without spread syntax, you would have to do it indirectly through partial application:

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="val1";
   this.prop2="val2";
};

var myArguments = ["hi", "how", "are", "you", "mr", null];
var myConstructorWithArguments = applyAndNew(myConstructor, myArguments);

console.log(new myConstructorWithArguments);
// (internal log of myConstructor):           arguments.length: 6
// (internal log of myConstructor):           ["hi", "how", "are", "you", "mr", null]
// (log of "new myConstructorWithArguments"): {prop1: "val1", prop2: "val2"}

Rozwinięcie w literałach tablicowych

Potężniejszy literał tablicowy

Bez składni rozwinięcia, aby utworzyć nową tablicę, używając tablicy już istniejącej jako jej części, składnia literału tablicowego nie jest już wystarczająca i musimy użyć kodu imperatywnego, używając kombinacji push, splice, concat itd. Z użyciem składni rozwinięcia staje się to o wiele prostsze i bardziej zwięzłe:

var czesci = ['kotek', 'na', 'plotek']; 
var wierszyk = ['wlazl', ...czesci, 'i', 'mruga']; 
// ["wlazl", "kotek", "na", "plotek", "i", "mruga"]

Tak jak dla list argumentów w wywołaniach funkcji, operator ... może być użyty wielokrotnie i w każdym miejscu literału tablicowego.

Kopia tablicy

var tab = [1, 2, 3];
var tab2 = [...tab]; // jak tab.slice()
tab2.push(4); 

// tab2 staje się [1, 2, 3, 4]
// tab pozostaje niezmieniona

Uwaga: Składnia rozwinięcia skutecznie sięga tylko na jeden poziom wgłąb przy kopiowaniu tablicy. W związku z tym takie podejście może być nieodpowiednie przy kopiowaniu tablic wielowymiarowych, jak pokazuje poniższy przykład (tak samo jest z Object.assign()  i kładnią rozwinięcia).

var a = [[1], [2], [3]];
var b = [...a];
b.shift().shift(); // 1
// Tablica a została zmodyfikowana: [[], [2], [3]]

Lepszy sposób na łączenie tablic

Array.concat jest często używane do dołączania elementów jednej tablicy na koniec drugiej. Bez składni rozwinięcia wygląda to tak:

var tab1 = [0, 1, 2];
var tab2 = [3, 4, 5];
// Dołącz wszystkie elementy tab2 na koniec tab1
tab1 = tab1.concat(tab2);

Przy użyciu składni rozwinięcia wygląda to natomiast tak:

var tab1 = [0, 1, 2];
var tab2 = [3, 4, 5];
tab1 = [...tab1, ...tab2];

Array.unshift jest często używane do dołączania elementów jednej tablicy na początek drugiej. Bez składni rozwinięcia wygląda to w następujący sposób:

var tab1 = [0, 1, 2];
var tab2 = [3, 4, 5];
// Dodaj wszystkie elementy tab2 na początek tab1
Array.prototype.unshift.apply(tab1, tab2) // tab1 staje się [3, 4, 5, 0, 1, 2]

Przy użyciu składni rozwinięcia otrzymuje to następującą postać [jednak zauważ, że w tym przypadku utworzona zostaje nowa tablica tab1 – w odróżnieniu od Array.unshift, tab1 nie jest automatycznie modyfikowana):

var tab1 = [0, 1, 2];
var tab2 = [3, 4, 5];
tab1 = [...tab2, ...tab1]; // tab1 staje się [3, 4, 5, 0, 1, 2]

Rozwinięcie w literałach tablicowych

The Rest/Spread Properties for ECMAScript proposal (stage 4) adds spread properties to object literals. It copies own enumerable properties from a provided object onto a new object.

Shallow-cloning (excluding prototype) or merging of objects is now possible using a shorter syntax than 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 }

Note that Object.assign() triggers setters whereas spread syntax doesn't.

Note that you cannot replace nor mimic the Object.assign() function:

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 } }

In the above example, the spread syntax does not work as one might expect: it spreads an array of arguments into the object literal, due to the rest parameter.

Tylko dla obiektów iterowalnych

Składnia rozwinięcia może być użyta jedynie dla obiektów iterowalnych:

var obiekt = {'klucz1': 'wartosc1'};
var tablica = [...obiekt]; // TypeError: obiekt is not iterable

Rozwinięcie z wieloma wartościami

Kiedy używaż składni rozwinięcia do wywołań funkcji, musisz być świadomy możliwości przekroczenia limitu liczby argumentów w silniku JavaScript. Po więcej szczegółów zajrzyj do apply().

Składnia reszty (parametry)

Składnia reszty ang. rest syntax) wygląda dokładnie jak składnia rozwinięcia, ale jest używana do destrukturyzacji tablic i obiektów. W pewnym sensie składnia reszty jest przeciwieństwem składni rozpakowania: rozwinięcie „rozpakowuje” elementy tablicy, natomiast składnia reszty „zbiera” wiele elementów i „pakuje” je do pojedynczego elementu. Zobacz: rest parameters.

Specyfikacje

Specification Status Comment
ECMAScript 2015 (6th Edition, ECMA-262) Standard Defined in several sections of the specification: Array Initializer, Argument Lists
ECMAScript 2018 (ECMA-262) Standard Defined in Object Initializer
ECMAScript Latest Draft (ECMA-262) Draft No changes.
ECMAScript Latest Draft (ECMA-262) Draft No changes.

Wsparcie przeglądarek

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.1Samsung 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.

Zobacz też