Uwaga! Trwają prace nad nową wersją serwisu. Mogą występować przejściowe problemy z jego funkcjonowaniem. Przepraszam za niedogodności!

🔥 Webinar Q&A na temat IT – pytaj, o co chcesz! już 24.11.2022 (czwartek) o 20:30

Rozproszenie i parametr reszty w JavaScript – jak ich nie mylić

Zrozum, co dzieje się w kodzie

Wyglądają tak samo – to trzy kropki – a jednak są wykorzystywane do innych celów. Teoretycznie nie trzeba znać ich nazw, by umieć je stosować, lecz warto usystematyzować swoją wiedzę zanim o rozproszenie lub parametr reszty zapyta Cię rekruter techniczny!

Spis treści

 Dlaczego warto usystematyzować wiedzę

Rozproszenie i parametr reszty wyglądają tak samo: to trzy kropki. W pewnym uproszczeniu mają jednak „przeciwne” role, które często u osób początkujących powodują problem z nazewnictwem. Czym jest jedno, a czym drugie? Warto umieć je nie tylko stosować, ale również odróżnić – choćby po to, by nie zaliczyć wpadki na rozmowie rekrutacyjnej (np. podczas omawiania kodu w trakcie pair programmingu).

 Rozproszenie (ang. spread)

„Rozprasza” elementy tam, gdzie dostajemy je „w pakiecie” – czyli w postaci stringa, tablicy czy obiektu. Spread często używany jest np. przy tworzeniu jednej tablicy z dwóch lub łączeniu obiektów.

 Rozproszenie obiektu

Przydaje się np. w pracy z Reactem przy przekazywaniu wartości między komponentami przez tzw. props. Załóżmy, że otrzymaliśmy dwa obiekty z ustawieniami dla stylów i chcemy je połączyć w jeden obiekt styles. Zrobimy to w ten sposób:

const spaceStyles = {
  margin: "60px",
  padding: "10px"
};
const sizeStyles = {
  width: "100px",
  height: "150px"
};
styles = { ...spaceStyles, ...sizeStyles };

Teraz obiekt styles wygląda tak (kolejność kluczy nie ma znaczenia, może się zmienić):

{
  margin: "60px",
  padding: "10px",
  width: "100px",
  height: "150px"
}

Rozproszenie obiektu

Uwaga: rozproszenie pozwala nam tworzyć nowe obiekty, ale nie pozwala mutować tych, które już mamy – oczywiście jeśli zgodnie z dobrymi praktykami stosujemy const (sprawdź przykład poniżej). Do tego nadal trzeba wykorzystywać metodę Object.assign():

const styles = {
  margin: "60px",
  padding: "10px",
  width: "100px",
  height: "150px"
}

const colorStyles = {
  color: "red",
  backgroundColor: "yellow"
}

Object.assign( styles, colorStyles );

Widzisz? W takiej sytuacji nie tworzymy nowego obiektu, lecz dodajemy nowe wartości do już istniejącego (mutujemy go). Nasz obiekt styles wygląda teraz tak:

{
  margin: "60px",
  padding: "10px",
  width: "100px",
  height: "150px",
  color: "red",
  backgroundColor: "yellow"
}

 Rozproszenie tablicy

W przykładzie mamy dwa „pakiety owoców”: tablicę yellowFruit i greenFruit.

const yellowFruit = ["melon", "banana", "lemon"];

const greenFruit = ["apple", "grapes"];

Chcemy połączyć je w ogólny zbiór owoców, np. tablicę fruit. Dzięki rozproszeniu robimy to w jednej linii i zachowujemy bardzo dobrą czytelność kodu:

const fruit = [ ...yellowFruit, ...greenFruit ];

Co otrzymujemy? Tylko jedną tablicę, ponieważ wartości zgromadzone w tablicach yellowFruit i greenFruit zostały rozproszone – „pozbyliśmy się ich opakowania” w postaci tablic:

["melon", "banana", "lemon", "apple", "grapes"]

Rozproszenie tablicy

Uwaga: tutaj do mutowania tablicy (gdy nie chcemy tworzyć nowej) zastosujemy odpowiednie metody, np. .push() czy .unshift() w połączeniu z rozproszeniem:

const fruit = ["apple", "pear"];
const yellowFruit = ["melon", "banana", "lemon"];

fruit.push(...yellowFruit);

W tej sytuacji nie tworzymy nowej tablicy, lecz rozszerzamy pierwszą o nowe elementy. Tablica fruit wygląda teraz tak:

["apple", "pear", "melon", "banana", "lemon"];

 Rozproszenie stringu

Podobnie jak w przypadku destrukturyzacji, rozproszenie można zastosować także do stringu:

const word = "mouse";

const letters = [ "a", "p", ...word, "d"];

W ten sposób dodaliśmy litery do nowo utworzonej tablicy:

["a", "p", "m", "o", "u", "s", "e", "d"]

 Kopiowanie elementów przez rozproszenie

Niejednokrotnie spotkasz się z tym, że rozproszenie używane jest do tworzenia kopii tablic i obiektów, np. tak:

// kopiowanie obiektu
const style = {
  margin: "60px",
  padding: "10px"
}
const newStyle = { ...style }

// kopiowanie tablicy
const numbers = [ 1, 2, 3, 4, 5 ]
const newNumbers = [ ...numbers ]

Trzeba jednak pamiętać, że w przypadku obiektów dochodzi w ten sposób do utworzenia kopii płytkiej (shallow copy) – tak samo jak wtedy, gdy stosujemy metodę Object.assign(). Jeśli chcesz zgłębić ten temat, zapraszam Cię do mojego wideo, w którym omawiam płytkie i głębokie kopiowanie obiektu.

 Rozproszenie w destrukturyzacji

O wykorzystaniu rozproszenia podczas destrukturyzacji przeczytasz w moim artykule Czym jest destrukturyzacja w JavaScript i jak ją stosować.

 Parametr reszty (ang. rest parameter)

Słowo klucz, które pozwoli Ci rozróżnić rest od spread, to „parametr”. Gdzie mamy parametry? Tylko podczas tworzenia funkcji (czy to w formie deklaracji funkcji czy wyrażenia funkcyjnego). Zaznaczam: tylko podczas tworzenia funkcji. Gdy wywołujemy funkcję, mamy już do czynienia z argumentami (nie parametrami!).

Przykładowo to są parametry a i b oraz c i d:

function sum( a, b ) {
  return a + b
}
const subtract = function( c, d ) {
  return c - d
}

A to argumenty 2 i 10 oraz 10.5 i 0.5:

sum( 2, 10 )

subtract(10.5, 0.5)

Jeśli nadal ich nie rozróżniasz, zajrzyj do ostatniej sekcji mojego artykułu „Front end – pojęcia, które mylą się na początku nauki (część 1)”.

Ustaliliśmy zatem, że parametr reszty to parametr funkcji. Mówiliśmy już też, że stanowi pewnego rodzaju „przeciwieństwo” rozproszenia. W rozproszeniu mieliśmy „pakiet” wartości, które „rozrzucaliśmy”. Parametr reszty zaś działa na odwrót: zbiera takie „rozrzucone” elementy w „pakiet” – tablicę (tak, tym razem to zawsze tylko tablica).

W poniższym przykładzie mamy funkcję, która wyświetla (dodaje do elementu body) listę zakupów o konkretnej nazwie. Zwróć uwagę, że nasz parametr reszty (...products) jest ostatnim parametrem – to warunek konieczny!

function displayList(name, ...products) {
  const ulElement = document.createElement("ul");
  ulElement.innerText = name;

  products.forEach(function(product) {
    const liElement = document.createElement("li");
    liElement.innerText = product;

    ulElement.appendChild(liElement);
  })
  document.body.appendChild(ulElement)
}

Dzięki takiemu rozwiązaniu za jednym zamachem możemy stworzyć długa listę produktów, wpisując kolejne argumenty za nazwą listy:

displayList("Grocery", "bananas", "apples", "cheese", "crisps", "pasta")

Uruchom powyższy kod np. w JSFiddle, by sprawdzić, w jaki sposób działa.

Parametr reszty

Chcesz przetestować kod z grafiki? Możesz skopiować poniższy przykład:

function createPerson( name, ...nicknames) {
   let fullName = name;
   nicknames.forEach( nick => {
      fullName = fullName + ' ' + nick
   })
   return fullName
}

const person = createPerson("John", "Cat", "The Greatest");
console.log(person)

 Co jest czym – podsumowanie

Rozproszenie (spread) to wyjęcie wartości z „opakowania” (tablicy, obiektu czy stringa), by można było przekazać je gdzieś indziej.

Parametr reszty (rest) to parametr funkcji, pod który podczas wywołania funkcji możemy podstawić dowolną liczbę argumentów.

Udostępnij ten artykuł:

Mentoring to efektywna nauka pod okiem doświadczonej osoby, która:

  • przekazuje Ci swoją wiedzę i nadzoruje Twoje postępy w zdobywaniu umiejętności,
  • uczy Cię dobrych praktyk i wyłapuje złe nawyki,
  • wspiera Twój rozwój i zwiększa zaangażowanie w naukę.