Domyślne atrybuty funkcji

Nowa wersja JS to nie tylko nowe rzeczy, ale przede wszystkim zmiany, które ułatwiają działania na często wykonywanych czynnościach.

Jak już poznałeś, nawet jeżeli funkcja oczekuje jakiś parametrów, nie musimy ich do niej przekazywać:


function myF(a, b) {
    console.log(a, b);
}

myF(); //undefined, undefined

W klasycznym JS jeżeli chcielibyśmy ustawić domyślne wartości takich parametrów skorzystalibyśmy z mniej więcej takiej instrukcji:


function myF(a, b) {
    if (typeof a === "undefined") a = 10;
    if (typeof b === "undefined") b = 5;

    console.log(a, b);
}

myF(); //10, b

W ES6 ustawianie domyślnych wartości dla parametrów jest o wiele prostsze:


function print(txt = "lorem") {
    console.log(txt);
}

print("kot"); //kot
print(); //lorem

Spread syntax

Spread syntax, to nowy zapis, który umożliwia rozbijanie iterowanej (takiej po której można robić pętle) wartości na składowe.
Co to jest ta iterowana wartość? Może nią być string (bo składa się z poszczególnych liter), może to być tablica (bo składa się z elementów), mogą to być kolekcje (po których bardzo często robiliśmy pętle for).

Poniżej kilka przykładów użycia tego zapisu:


//rozbijanie tablicy na poszczególne liczby
const tab = [1,2,3,4];
console.log(...tab);

//kopiowanie tablic
const tab2 = [...tab]; //kopia tablicy tab
tab2 = tab.slice(); //starsza metoda

//łączenie tablic
const tabPart = [3, 4]
const tabFull = [1, 2, ...tabPart, 5, 6]; //1,2,3,4,5,6

//rozdzielanie teksty na poszczególne litery
const str = "Ala ma";
console.log(...str); //A l a m a

Math.max wymaga parametrów po przecinku (nie jako tablica), więc mogę tutaj zastosować spread:


const Math.max(...tab);
Math.max.call(Math, tab); //to samo co powyższe

Spread mogę też wykorzystać do zamiany kolekcji elementów na tablicę:


const divs = document.querySelector('div');

//za pomocą call
[].forEach.call(divs, function(div) {
    console.log(div);
})

//za pomocą spread
[...divs].forEach(div => {
    console.log(div);
})

Rest parameter

Identycznie jak spread syntax wygląda rest parameter.
Zapis ten umożliwia zbieranie w jedną zmienną (będącą tablicą) wielu parametrów przekazywanych do funkcji:


function myF(...param) {
    console.log(param); //[1, 2, 3, 4, 5]
}

myF(1,2,3,4,5);

function myF(...param) { //to jest rest parameter

    //tworze sobie nową tablicę na bazie rest parameter
    const newTab = [...param]; //to jest spreat syntax
    newTab.push(6);
    console.log(param, newTab); //[1,2,3], [1,2,3,6]
}

myF(1,2,3);

Tutaj może ci się pojawić pytanie. Po co takie dziwy, skoro poznaliśmy już właściwość arguments, która przecież przechowują przekazane atrybuty:


function myF() {
    console.log(arguments); //1,2,3,4,5
}

myF(1,2,3,4,5);

Po pierwsze właściwość arguments ma narzuconą nazwę, a rest możemy nazywać tak jak nam pasuje. Powyżej nazwaliśmy go param, ale kto nam broni nazwać to inaczej.
Druga różnica jest taka, że do rest parameters trafiają parametry w formie tablicy.
Mimo, że po arguments możemy zrobić pętlę for, nie jest on tablicą (tak samo jak w przypadku kolekcji):


function myF1() {
    arguments.forEach(el => console.log(el));
}
myF1(1,2,3,4,5); //blad - arguments.forEach is not a function


function myF2(...attr) {
    attr.forEach(el => console.log(el));
}
myF2(1,2,3,4,5); //wszystko ok

Trzecią - najważniejszą zmianą jest to, że do rest możemy bardzo łatwo zbierać "pozostałe atrybuty". Wyobraź sobie, że twoja funkcja wymaga 2 atrybutów, ale ktoś zechce przekazać do niej więcej:


function printAbout(name = "Ala", pet = "kot", ...other) {
    console.log("To jest " + name);
    console.log("Jej ulubione zwierze to: " + pet);

    if (other.length) {
        console.log("Ala ma jeszcze takie zwierzaki: ", other.join(', '));
    }
}

printAbout(undefined, undefined, "pies", "świnka", "chomik");

Jasne - to samo spokojnie osiągniesz w starszym JS, ale jest to ciut trudniejsze, bo musielibyśmy skonwertować resztę argumentów na tablicę (wykorzystamy do tego call):


function printAbout(name, pet) {
    if (typeof name == "undefined") name = "Ala";
    if (typeof pet == "undefined") name = "kot";

    console.log("To jest " + name);
    console.log("Jej ulubione zwierze to: " + pet);

    if (arguments.length > 2) {
        const pets = [].slice.call(arguments, 2);
        console.log("Ala ma jeszcze takie zwierzaki: ", args.join(', '));
    }
}

printAbout(undefined, undefined, "pies", "świnka", "chomik");

Pamiętaj, że rest musi występować jako ostatni w parametrach:


function myF(a, b, ...numbers) {

}

function myF(a, ...numbers, b) { //błąd : Rest parameter must be last formal parameter

}

W sumie to logiczne. Skąd funkcja miała by wiedzieć kiedy kończysz podawanie numbers? No ok - to też tak naprawdę dało by się przeskoczyć tnąc atrybuty.

Ps. Pewnie zauważyłeś, że lubię kończyć zdania trzema kropkami? Nie ma to nic wspólnego z rest ani spread. Obiecuję. ...Uhh - a raczej U h h

Trening czyni mistrza

Poniżej zamieszczam kilka zadań, które w ramach ćwiczenia możesz wykonać.

  1. Stwórz 2 tablice:
    
                const tab1 = [1, 2, 3, 6, 5];
                const tab2 = [10, 20, 15, 4];
            

    Za pomocą spread syntax połącz te dwie tablice tak, by w wyniku uzyskać:

    
                const tab3 = [1, 2, 3, 6, 5, 10, 20, 15, 4];
            

    Posortuj te tablice odpowiednią metodą.

    Następnie pobierz z tej tablicy największą i najmniejszą liczbę. Wykorzystaj do tego obiekt Math

    
                const tab1 = [1, 2, 3, 6, 5];
                const tab2 = [10, 20, 15, 4];
                const tab3 = [...tab1, ...tab2];
    
                tab3.sort();
                console.log(Math.max(tab3));
                console.log(Math.min(tab3));
                
  2. Stwórz funkcję printBig(), która będzie przyjmowała dowolną liczbę parametrów. Wykorzystaj do tego rest parameter. Wypisz w konsoli jeden tekst, który będzie składał się z przekazanych wartości pisanych dużymi literami i rozdzielonych znakiem " + ":

    
                wywołanie: printBig("pies", "świnka", "kot");
                wynik: "PIES + ŚWINKA + KOT"
            
    
            function printBig(...args) {
                const tab = args.map(el => el.toUpperCase());
                return tab.join(" + ");
            }
    
            console.log( printBig("pies", "świnka", "kot") );