Dekompozycja

Dekompozycja to nowa funkcjonalność w ES6, która polega na wyciąganiu zmiennych z jakiejś struktury - np z obiektów lub tablic.

Dekompozycja obiektów

Przypuśćmy, że właśnie pobraliśmy ajaxem obiekt z danymi.


const myObject = {
    name : "Marcin",
    surname : "Kowalski",
    age : 10
}

Jeżeli teraz chciałbym, z tego obiektu wyciągnąć dane i podstawić je pod zmienne:


const name = myObject.name;
const surname = myObject.surname;
const age = myObject.age;

Ciut dużo pisania (ok, to tylko prosty przykład). W ES6 możemy sobie to uprościć stosując dekompozycję:


const { name, surname, age } = myObject;

To co napisaliśmy po lewej stronie przypomina obiekt, ale nim nie jest. Jest to wzór, pod który następnie podstawiamy jakiś obiekt (u nas myObject).
Między klamrami podajemy nazwy zmiennych, pod które podstawione zostaną właściwości o takich samych nazwach. Jeżeli danej właściwości w podstawionym obiekcie nie będzie, wtedy pod daną zmienną zostanie podstawione undefined:


const { name, surname, age, favoritePet } = myObject;

console.log(name); //Marcin
console.log(surname); //Kowalski
console.log(age); //10
console.log(favoritePet); //undefined

W powyższym przykładzie tworzymy zmienne o takich samych nazwach jak właściwości myObject. Gdybyś chciał stworzyć zmienne o innych nazwach, musisz użyć znaku dwukropka:


const {
    name: newName,
    surname: newSurname,
    age,
    favoritePet
} = myObject;

console.log(newName); //Marcin
console.log(newSurname); //Kowalski
console.log(age); //10
console.log(favouritePet); //undefined

Dodatkowo tworząc wzorzec, możemy danym zmiennym przypisać domyślne wartości, które zostaną użyte, jeżeli danej właściwości w podstawianym obiekcie nie będzie:


const {
    name: newName,
    surname: newSurname,
    age = 14,
    favoritePet = "Szamson"
} = myObject;

console.log(newName); //Marcin
console.log(newSurname); //Kowalski
console.log(age); //10 - bo właściwość istnieje
console.log(favouritePet); //Szamson - została użyta domyślna

Ale po co to się może przydać? Przede wszystkim tak jak wyżej - do wyciągania zmiennych z obiektów. Wyobraź sobie że chcesz wziąć coś z obiektu, a całą resztę zostawić w spokoju. Tworzysz więc szybki wzór, podstawiasz pod dany obiekt i włala. Cała reszta Cię nie interesuje.


const { url, print } = uberBigObjectWithMilionPropertiesAndMethods;

Pamiętaj też, że taki wzór możemy używać dla wielu obiektów. Wyobraź sobie, że robisz pętlę po pobranej tabeli użytkowników.
Każdego użytkownika podstawiasz pod wzór, a następnie działasz na zmiennych. Nie każdy użytkownik ma swój email, nie każdy ma swoją stronę www, ale to niczemu nie przeszkadza, bo w razie czego wzór dobierze domyślne wartości:


.fetch(.....)
.then(result => result.json())
.then(result => {
    //dzialamy na pobranych użytkownikach
    result.users.forEach(el => {
        const {
            name = "",
            surname = "",
            email = "",
            plan = "basic"
            role = "user",
            www = "http://"
        } = el;

        console.log(surname, name, email, plan, role, www);
    });
});

Dekompozycja tablic

Podobnymi zasadami rządzi się dekompozycja tablic. Są małe różnice, ale ogólna zasada jest podobna.

Przypuśćmy, że mamy tablicę, z której chcielibyśmy pobrać wartości:


const tab = [1, 2, 3, 4];

Jeżeli teraz chciałbym pobrać z tej tablicy 2 pierwsze wartości to użył bym zapisu:


const tab = [1, 2, 3, 4];

//klasycznie
const a = tab[0];
const b = tab[1];

//za pomocą dekompozycji
[a, b] = tab;

Jeżeli chcielibyśmy z tablicy wyciągnąć wartości, ale z pominięciem kolejnej, wtedy musimy użyć przecinka:


const tab = [1, 2, 3, 4];

[a, b, , c] = tab;

console.log(a, b, c); //1, 2, 4

Tak samo jak w przypadku dekompozycji obiektów, tworzymy jakiś wzór pod które potem podstawiana jest tablica.

Podobnie też możemy definiować domyślne wartości:


const tab = [1, 2, , 4];

[a, b, c=5] = tab;

console.log(a, b, c); //1, 2, 5

Co ciekawe, przy dekompozycji tablic możemy też użyć rest:


const tab = [1, , 3, 4, 5];

[a, ...r] = tab;

console.log(a, r); //1, [undefined, 3, 4, 5]

Bardziej skomplikowany przykład

Przypuśćmy, że mamy obiekt użytkownika, który chcemy poddać dekompozycji.

Znowu - załóżmy, że całość wykonywana jest w pętli po tablicy użytkowników, które wczytaliśmy z json z serwera. W poniższym przykładzie skupiamy się na pojedynczym użytkowniku.


const myObj = {
    name : "Marcin",
    surname : "Domański",
    role : "webmaster",
    pets: ["pies", "kot"],
    car : {
        name : "Honda",
        year: 2002,
        type : "hatchback"
    }
}

Z dekompozycją name, surname, role i pets nie będzie problemu. Ale jak poddać dekompozycji bardziej skomplikowane struktury? Musimy we wzorze określić ich wygląd, czyli jeżeli poddajemy dekompozycji tabele w obiekcie, musimy w naszym wzorze stworzyć odzwierciedlenie takiej tabeli w obiekcie. To samo tyczy się obiektów w obiektach:


const {
    name, //nie chcemy zmieniać tej zmiennej
    surname, //tej tez nie
    role,
    pets : [
        pet1,
        pet2
    ],
    car : {
        name : carName,
        year : carYear,
        type : carType
    }
} = myObj || {}

Zauważ że nasz wzorzec postawiłem pod myObj || pusty obiekt. Dzięki temu zabezpieczam się na wypadek, gdyby nie było myObj

Trening czyni mistrza

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

  1. Mamy obiekt:
    
                const obj = {
                    name : "Marcin",
                    surname : "Kowalski",
                    age : 20
                }
                

    Za pomocą dekompozycji utwórz 3 zmienne: name, surname i userAge

    
                const obj = {
                    name : "Marcin",
                    surname : "Kowalski",
                    age : 20
                }
    
                const {name, surname, age : userAge} = obj;
    
                console.log(name, surname, userAge);
                
  2. Mamy tablicę:
    
                const tab = [1,2,3,4,5];
            
    Za pomocą dekompozycji pobierz pierwszą, drugą i czwartą wartość z tej tablicy i podstaw je pod zmienne a, b, c
    
                    const tab = [1,2,3,4,5];
    
                    const [a, b, , c] = tab;
    
                    console.log(a, b, c);