Lekcja 13: Nowoczesny JavaScript (ES6+) - Część 2
Kontynuujemy eksplorację funkcji wprowadzonych w ECMAScript 2015 (ES6) i nowszych wersjach, które ułatwiają pisanie bardziej zwięzłego, czytelnego i wydajnego kodu JavaScript.
Parametry domyślne (Default Parameters)
ES6 wprowadziło możliwość definiowania domyślnych wartości dla parametrów funkcji. Jeśli argument dla danego parametru nie zostanie przekazany podczas wywołania funkcji (lub zostanie przekazany jako undefined
), użyta zostanie wartość domyślna.
// Stary sposób (przed ES6)
function przywitajStare(imie) {
imie = (typeof imie !== 'undefined') ? imie : 'Gościu';
console.log(`Witaj (stare), ${imie}!`);
}
// Nowy sposób (ES6)
function przywitajNowe(imie = 'Gościu', powitanie = 'Witaj') {
console.log(`${powitanie}, ${imie}!`);
}
przywitajStare("Anna"); // Witaj (stare), Anna!
przywitajStare(); // Witaj (stare), Gościu!
przywitajNowe("Piotr"); // Witaj, Piotr!
przywitajNowe(); // Witaj, Gościu!
przywitajNowe("Zofia", "Cześć"); // Cześć, Zofia!
przywitajNowe(undefined, "Dzień dobry"); // Dzień dobry, Gościu!
// Wartości domyślne mogą być wyrażeniami
function obliczPodatek(cena, stawka = 0.23) {
return cena * stawka;
}
console.log(obliczPodatek(100)); // 23
console.log(obliczPodatek(100, 0.08)); // 8
Operator Spread (`...`)
Operator spread (rozszerzenia) pozwala na "rozpakowanie" iterowalnych elementów (jak tablice, stringi) lub właściwości obiektów w miejscach, gdzie oczekiwanych jest zero lub więcej argumentów (dla wywołań funkcji) lub elementów (dla literałów tablicowych) lub par klucz-wartość (dla literałów obiektowych).
Spread w literałach tablicowych
Umożliwia łatwe łączenie tablic lub tworzenie kopii.
let czesci1 = [1, 2, 3];
let czesci2 = [4, 5, 6];
let dodatkowyElement = 0;
// Łączenie tablic
let polaczona = [dodatkowyElement, ...czesci1, ...czesci2, 7];
console.log(polaczona); // [0, 1, 2, 3, 4, 5, 6, 7]
// Kopiowanie tablicy (płytka kopia)
let kopiaCzesci1 = [...czesci1];
console.log(kopiaCzesci1); // [1, 2, 3]
kopiaCzesci1.push(4); // Modyfikacja kopii
console.log(czesci1); // [1, 2, 3] (oryginał bez zmian)
// Rozpakowanie stringa
let tekst = "ABC";
let litery = [...tekst];
console.log(litery); // ["A", "B", "C"]
Spread w wywołaniach funkcji
Pozwala przekazać elementy tablicy jako osobne argumenty do funkcji.
function sumaTrzech(a, b, c) {
return a + b + c;
}
let liczbyDoSumy = [5, 10, 15];
// Stary sposób
// let wynikStary = sumaTrzech.apply(null, liczbyDoSumy);
// Nowy sposób (spread)
let wynikNowy = sumaTrzech(...liczbyDoSumy);
console.log(wynikNowy); // 30
// Przykład z Math.max()
let liczbyMax = [2, 8, 1, 15, 4];
let max = Math.max(...liczbyMax);
console.log(max); // 15
Spread w literałach obiektowych (ES2018)
Umożliwia łatwe kopiowanie lub łączenie obiektów.
let ustawieniaPodstawowe = {
motyw: "jasny",
jezyk: "pl"
};
let ustawieniaUzytkownika = {
jezyk: "en", // Nadpisze jezyk z ustawień podstawowych
pokazPowiadomienia: true
};
// Łączenie obiektów
let finalneUstawienia = { ...ustawieniaPodstawowe, ...ustawieniaUzytkownika, ostatnieLogowanie: Date.now() };
console.log(finalneUstawienia);
// { motyw: 'jasny', jezyk: 'en', pokazPowiadomienia: true, ostatnieLogowanie: ... }
// Kopiowanie obiektu (płytka kopia)
let kopiaUstawien = { ...ustawieniaPodstawowe };
console.log(kopiaUstawien);
Ważne: Kolejność ma znaczenie. Właściwości z późniejszych obiektów nadpisują te z wcześniejszych, jeśli mają te same klucze.
Operator Rest (`...`)
Operator rest (reszty) wygląda tak samo jak spread (`...`), ale działa odwrotnie. Zbiera resztę elementów (w tablicach lub parametrach funkcji) lub właściwości (w obiektach) w jedną strukturę.
Rest w parametrach funkcji
Pozwala funkcji przyjąć dowolną liczbę argumentów jako tablicę. Musi być ostatnim parametrem w definicji funkcji.
// Funkcja sumująca dowolną liczbę argumentów
function sumujWszystko(...liczby) {
console.log("Zebrane liczby:", liczby); // liczby to tablica
let suma = 0;
for (const liczba of liczby) {
suma += liczba;
}
return suma;
}
console.log(sumujWszystko(1, 2, 3)); // Zebrane liczby: [1, 2, 3] -> 6
console.log(sumujWszystko(10, 20, 30, 40)); // Zebrane liczby: [10, 20, 30, 40] -> 100
console.log(sumujWszystko(5)); // Zebrane liczby: [5] -> 5
console.log(sumujWszystko()); // Zebrane liczby: [] -> 0
// Przykład z pierwszym argumentem osobnym
function logujWiadomosc(typ, ...komunikaty) {
console.log(`[${typ.toUpperCase()}]`);
komunikaty.forEach(msg => console.log(`- ${msg}`));
}
logujWiadomosc("info", "Uruchomiono aplikację", "Połączono z bazą danych");
// [INFO]
// - Uruchomiono aplikację
// - Połączono z bazą danych
Rest w destrukturyzacji tablic
Zbiera pozostałe elementy tablicy do nowej tablicy.
let dane = ["Jan", "Kowalski", 30, "Warszawa", "Polska"];
let [imie, nazwisko, ...resztaDanych] = dane;
console.log(imie); // "Jan"
console.log(nazwisko); // "Kowalski"
console.log(resztaDanych); // [30, "Warszawa", "Polska"]
Rest w destrukturyzacji obiektów (ES2018)
Zbiera pozostałe właściwości obiektu do nowego obiektu.
let config = {
host: "localhost",
port: 8080,
user: "admin",
password: "secret",
timeout: 5000
};
let { host, port, ...daneLogowania } = config;
console.log(host); // "localhost"
console.log(port); // 8080
console.log(daneLogowania); // { user: 'admin', password: 'secret', timeout: 5000 }
Ulepszone literały obiektowe (Enhanced Object Literals)
ES6 wprowadziło kilka skrótów składniowych ułatwiających definiowanie obiektów.
Skrócona składnia właściwości
Jeśli nazwa zmiennej jest taka sama jak nazwa właściwości, którą chcesz utworzyć, możesz podać tylko nazwę zmiennej.
let marka = "Ford";
let model = "Focus";
// Stary sposób
// let autoStare = {
// marka: marka,
// model: model
// };
// Nowy sposób (skrócona składnia)
let autoNowe = { marka, model };
console.log(autoNowe); // { marka: 'Ford', model: 'Focus' }
Skrócona składnia metod
Można pominąć słowo kluczowe `function` i dwukropek przy definiowaniu metod obiektu.
let kalkulator = {
// Stary sposób
// dodaj: function(a, b) {
// return a + b;
// },
// Nowy sposób (skrócona składnia)
dodaj(a, b) {
return a + b;
},
odejmij(a, b) {
return a - b;
}
};
console.log(kalkulator.dodaj(5, 3)); // 8
Obliczane nazwy właściwości
Można używać wyrażeń w nawiasach kwadratowych `[]` do dynamicznego definiowania nazw właściwości obiektu.
let nazwaWlasciwosci = "wiek";
let i = 0;
let osoba = {
imie: "Karol",
[nazwaWlasciwosci]: 40, // Nazwa właściwości obliczona ze zmiennej
[`id_${i + 1}`]: 12345 // Nazwa właściwości obliczona z wyrażenia
};
console.log(osoba); // { imie: 'Karol', wiek: 40, id_1: 12345 }
console.log(osoba.wiek); // 40
console.log(osoba.id_1); // 12345
Zadanie praktyczne
Stwórz dwie tablice: const owoce = ["jabłko", "banan"];
i const warzywa = ["marchew", "pomidor"];
. Użyj operatora spread, aby stworzyć nową tablicę jedzenie
, która będzie zawierać wszystkie elementy z obu tablic.
Następnie stwórz funkcję logItems
, która przyjmuje dowolną liczbę argumentów (użyj operatora rest) i wyświetla każdy argument w konsoli w osobnej linii.
Wywołaj funkcję logItems
, przekazując jej elementy z tablicy jedzenie
za pomocą operatora spread.
Pokaż rozwiązanie
const owoce = ["jabłko", "banan"];
const warzywa = ["marchew", "pomidor"];
// Utworzenie nowej tablicy za pomocą spread
const jedzenie = [...owoce, ...warzywa];
console.log("Połączona tablica jedzenie:", jedzenie);
// Funkcja z parametrem rest
function logItems(...items) {
console.log("--- Logowanie elementów ---");
items.forEach(item => {
console.log(item);
});
console.log("-------------------------");
}
// Wywołanie funkcji z argumentami spread
logItems(...jedzenie);
Zadanie do samodzielnego wykonania
Stwórz obiekt punkt
z właściwościami x
i y
(np. { x: 10, y: 20 }
). Następnie stwórz drugi obiekt kolorPunktu
z właściwością kolor
(np. { kolor: "czerwony" }
). Użyj operatora spread dla obiektów, aby stworzyć nowy obiekt kolorowyPunkt
, który będzie zawierał wszystkie właściwości z obu obiektów punkt
i kolorPunktu
. Wyświetl wynikowy obiekt w konsoli.
FAQ - Nowoczesny JavaScript (ES6+) - Część 2
Jaka jest główna różnica między operatorem Spread a Rest?
Chociaż używają tej samej składni (`...`), działają odwrotnie. Spread "rozpakowuje" elementy z kolekcji (tablicy, obiektu, stringu) do pojedynczych wartości. Rest "zbiera" wiele pojedynczych wartości (argumenty funkcji, elementy tablicy, właściwości obiektu) do jednej kolekcji (tablicy lub obiektu).
Czy mogę użyć operatora Rest w środku listy parametrów funkcji?
Nie, parametr rest musi być zawsze ostatnim parametrem w definicji funkcji. Zbiera on wszystkie pozostałe argumenty przekazane do funkcji, które nie zostały przypisane do wcześniejszych parametrów.
Czy operator Spread tworzy głęboką kopię (deep copy)?
Nie, zarówno dla tablic, jak i obiektów, operator spread tworzy tylko płytką kopię (shallow copy). Oznacza to, że jeśli elementy tablicy lub właściwości obiektu są same obiektami lub tablicami, kopiowana jest tylko referencja do nich, a nie ich zawartość.
Czy parametry domyślne działają, jeśli przekażę `null`?
Nie. Wartość domyślna parametru jest używana tylko wtedy, gdy argument nie zostanie przekazany lub zostanie przekazany jako `undefined`. Jeśli jawnie przekażesz `null`, to właśnie `null` zostanie użyte jako wartość parametru.
Czy skrócona składnia metod w obiektach zmienia zachowanie `this`?
Nie, skrócona składnia metod (np. mojaMetoda() { ... }
) jest tylko cukrem syntaktycznym dla tradycyjnego zapisu mojaMetoda: function() { ... }
. Zachowanie `this` w takich metodach jest takie samo jak w tradycyjnych funkcjach przypisanych jako metody obiektu.
Czy mogę używać obliczanych nazw właściwości dla metod?
Tak, można używać obliczanych nazw również przy definiowaniu metod w literałach obiektowych, np. { [`metoda_${id}`]() { /* ... */ } }
. Nazwa metody zostanie obliczona na podstawie wyrażenia w nawiasach kwadratowych.