Spread rozkłada obiekt wewnątrz innego
Składnia spread obiektów, czyli trzy kropki przed obiektem, kopiuje własne, przeliczalne właściwości tego obiektu do otaczającego literału obiektu. To najkrótsza droga do kopiowania, łączenia i tworzenia zmienionych kopii bez ruszania oryginału.
copy ma te same klucze i wartości co user, ale to inny obiekt. Zmiana jednego nie wpływa na drugi, przynajmniej na najwyższym poziomie. Do tego zastrzeżenia zaraz wrócimy.
Model myślowy: { ...obj } znaczy "wlej właściwości obj do tego nowego literału obiektu". Wszystko, co napiszesz obok spreadu, staje się częścią wyniku.
Kopiowanie i nadpisywanie w jednym kroku
Najczęstszy wzorzec to rozłożenie obiektu i dodanie lub nadpisanie kilku właściwości. Późniejsze klucze wygrywają, więc najpierw spread, a potem nadpisania:
user pozostaje nietknięty. updated to nowy obiekt z podmienionym role. Ten wzorzec niemutowalnej aktualizacji pojawia się wszędzie we współczesnym JavaScript: w aktualizacjach stanu w React, w reducerach Reduksa i w każdym kodzie, który woli nie modyfikować danych w miejscu.
Odwróć kolejność, a zachowanie będzie odwrotne:
Tutaj role: "guest" jest pierwsze, więc user.role je nadpisuje. Przydaje się, gdy chcesz mieć wartości domyślne, które rozkładany obiekt może nadpisać.
Łączenie obiektów
Rozłóż dwa (lub więcej) obiekty w nowym literale, żeby je połączyć. Przy powtarzających się kluczach późniejsze obiekty nadpisują wcześniejsze:
theme i fontSize pochodzą z userPrefs, a debug przechodzi z defaults. Trzy obiekty? Cztery? Ta sama zasada: czytasz od początku do końca, wygrywa ostatni zapis.
To współczesny zamiennik Object.assign({}, defaults, userPrefs). Oba robią to samo, ale wersja ze spreadem jest czytelniejsza i nie kusi do popularnego błędu, czyli napisania Object.assign(defaults, userPrefs), co modyfikuje defaults.
Spread to płytka kopia
To ta część, na której ludzie się potykają. Spread kopiuje właściwości obiektu z najwyższego poziomu. Jeśli wartość właściwości sama jest obiektem lub tablicą, kopiowana jest referencja, a nie zawartość.
Zmiana copy.address.city zmieniła też user.address.city, bo oba obiekty współdzielą ten sam obiekt address. Spread dał nam tylko nowe zewnętrzne opakowanie.
Kiedy musisz zmienić coś zagnieżdżonego, użyj spreadu na każdym poziomie, który zmieniasz:
Do prawdziwie głębokiej kopii dowolnych danych sięgnij po structuredClone(obj). Radzi sobie z zagnieżdżonymi obiektami, tablicami, datami, mapami i zbiorami, i jest wbudowany w każde współczesne środowisko uruchomieniowe.
Spread vs rest
Mają te same trzy kropki, ale robią odwrotne rzeczy. Spread rozkłada, rest zbiera.
Praktyczna zasada: jeśli ... stoi po lewej stronie = (destrukturyzacja), to rest. Jeśli jest w literale obiektu lub tablicy po prawej stronie, to spread.
Usuwanie właściwości bez mutacji
Połączenie destrukturyzacji z rest i spreadu daje czysty sposób na kopię obiektu bez jednego klucza, bez delete i bez mutacji:
tempToken trafia do osobnej zmiennej (którą ignorujesz), a cała reszta ląduje w safe. Oryginalny user pozostaje nietknięty.
Czego spread nie kopiuje
Kilka niuansów, które warto znać:
- Właściwości nieprzeliczalne (non-enumerable) nie są kopiowane. Większość tworzonych właściwości jest domyślnie przeliczalna, ale właściwości zdefiniowane przez
Object.definePropertyi niektóre wbudowane już nie. - Prototyp nie jest kopiowany.
{ ...instance }daje zwykły obiekt, a nie instancję oryginalnej klasy. Metod zdefiniowanych na prototypie klasy nie będzie w kopii. - Gettery są wywoływane. Spread obiektu z getterem wywołuje getter raz i zapisuje zwróconą wartość jako zwykłą właściwość nowego obiektu.
copy ma x i y, ale to zwykły obiekt: distance mieszka w Point.prototype, którego spread nie dotknął. Jeśli musisz skopiować instancję klasy, zwykle sama klasa musi udostępnić własną metodę clone.
Dalej: metody tablic
Spread to jeden element zestawu narzędzi do niemutowalnych danych. Drugi duży element to metody tablic: map, filter, reduce i spółka, które tworzą nowe tablice zamiast modyfikować oryginały. O nich na następnej stronie.
Najczęściej zadawane pytania
Co robi ...obj w JavaScript?
Wewnątrz literału obiektu ...obj kopiuje wszystkie własne, przeliczalne (enumerable) właściwości tego obiektu do nowego obiektu. { ...user } tworzy świeży obiekt z tymi samymi kluczami i wartościami co user. To standardowy sposób na kopiowanie lub łączenie obiektów bez modyfikowania oryginałów.
Jak połączyć dwa obiekty w JavaScript?
Rozłóż oba w nowym literale obiektu: const merged = { ...a, ...b }. Właściwości z b nadpisują pasujące klucze z a, bo wygrywa późniejsza właściwość. To odpowiednik Object.assign({}, a, b), ale czyta się to znacznie lepiej.
Czy spread obiektów tworzy głęboką kopię?
Nie. Spread obiektów tworzy płytką kopię: kopiuje właściwości najwyższego poziomu, ale zagnieżdżone obiekty i tablice nadal są współdzielone przez referencję. Zmiana copy.address.city zmieni też original.address.city. Do prawdziwej głębokiej kopii użyj structuredClone(obj).
Jaka jest różnica między spread a rest?
Wyglądają identycznie (...), ale robią odwrotne rzeczy. Spread rozkłada obiekt lub tablicę na pojedyncze właściwości lub elementy: { ...user }. Rest zbiera resztę w jedną zmienną: const { name, ...others } = user. To kontekst mówi, z którym masz do czynienia.