Obiekty i this

W JavaScript prawie wszystko jest obiektem, a my w naszych skryptach nie raz i nie dwa odwołujemy się do obiektów. Dla przykładu window, console, Math to typowe obiekty, które posiadają swoje metody i właściwości:


const tab = [1,2,3];
tab.length //widzisz kropkę? Przez nią odwołujemy się do metod lub właściwości obiektu - w tym przypadku obiektu typu Array

const text = "Ala ma kota";
console.log(  text.charAt(0) ); //text - typ prosty został na chwilę zamieniony na obiekt (po czym od razu po wykonaniu akcji wrócił do typu prostego)

window.innerHeight //właściwość innerHeight obiektu window

Math.floor(21.3); //metoda obiektu Math

document.cookie //właściwość cookie obiektu document

(new Date()).getFullYear(); //metoda getFullYear() obiektu Date;

button.innerHTML //właściwość innerHTML obiektu button

Obiekty można traktować jako pewne zamknięte pudełka. Weźmy na przykład psa. Taki obiekt ma brązową sierść, cztery łapy, brązowe oczy. To jego właściwości. Potrafi też biegać, jeść, szczekać. To jego czynności - w naszym języku zwane funkcjami czy metodami. A wszystko to upakowane w jeden obiekt - zwany psem. Dzięki temu jeżeli chcemy zrobić coś z psem - np. go umyć czy zabrać do parku, mamy dostęp do tych wszystkich rzeczy pod postacią pojedynczego psiego obiektu.

obiekt pies

Przykład bliższy naszej Frontendowej działce to chociażby pierwszy lepszy element na stronie - np. pojedynczy button. Ma właściwości określające jego zawartość (np. tekst), szerokość, wysokość, pozycję na stronie. Ale też ma metody, czyli funkcje które może wykonać - np. focus czy blur, które go zaznaczą lub odznaczą gdy zostaną odpalane.

Tworzenie pojedynczego obiektu

Poza dostępem do gotowych obiektów, możemy tworzyć też swoje własne.

Obiekty możemy podzielić na pojedyncze instancje (pojedyncze egzemplarze), oraz grupy obiektów o podobnych właściwościach (np. tablice, linki, buttony, stringi itp).

Na początku zajmijmy się pojedynczymi instancjami.

Aby utworzyć taki obiekt możemy skorzystać z poniższej konstrukcji:


const dog = {
    name: "Szama",
    speed: 1000,
    showText: function() {
        return "Lubię walczyć ze złem";
    }
}

Przy definiowaniu takiego literału obiektu właściwości rozdzielamy przecinkiem, a zamiast znaku równości stosujemy dwukropek.

W dzisiejszych czasach metody dla obiektów możemy też definiować w skrócony sposób z pominięciem słowa function:


//zamiast
const dog = {
    name: "Szama",
    speed: 1000,
    showText: function() {
        return "Lubię walczyć ze złem";
    }
}

//możemy
const dog = {
    name: "Szama",
    speed: 1000,
    showText() {
        return "Lubię walczyć ze złem";
    }
}

Ale też jeżeli pod klucze podstawiamy wartości z jakiś zmiennych, których nazwy są takie same jak danych kluczy, możemy taki zapis skrócić:


const name = "Szama";
const speed = 1000;

//zamiast
const dog = {
    name: name,
    speed: speed,
    showText() {
        return "Lubię walczyć ze złem";
    }
}

//możemy napisać
const dog = {
    name,
    speed,
    showText() {
        return "Lubię walczyć ze złem";
    }
}

Zastosowanie tej małej właściwości - niby skromne, a ma sporą moc:


const tab = [];
const name = "Szama";
const speed = 1000;

//zamiast
const ob = {
    name: name,
    speed: speed
}
tab.push(ob);

//mogę
tab.push({
    name: name,
    speed: speed
});

//lub jeszcze lepiej
tab.push({name, speed});

Odwoływanie się do właściwości

Do właściwości i metod obiektu możemy się odwoływać na dwa sposoby:


const dog = {
    name: "Szama",
    speed: 1000,
    showText() {
        return "Lubię walczyć ze złem";
    }
}

//poprzez kropkę po której podajemy nazwę klucza
dog.name; //"Szama"
dog.speed; //1000
dog.showText(); //"Lubię walczyć ze złem"

//używając kwadratowych nawiasów
dog["name"]; //"Szama"
dog["speed"]; //1000
dog["showText"](); //"Lubię walczyć ze złem"

Najczęściej będziemy korzystać z notacji z kropką. Nie oznacza to jednak, że drugi sposób nie ma zastosowania.

Nazwy kluczy dla obiektów w większości przypadków przypominać będą nazwy zwykłych zmiennych. Jeżeli tak jest, możemy odwoływać się do nich notacją z kropką. Nie zawsze jednak będzie to możliwe.

Klucze obiektów przyjmują postać dowolnego tekstu lub Symbolu. Oznacza to, że mogą zdarzyć się sytuacje, gdzie klucz będzie miał wartość, do której nie będziemy się mogli dostać za pomocą notacji z kropką:


const calendar = {
    "2021-11-11" : "Narodowe Święto Niepodległości"
}

calendar.2021-11-11 //oczywisty błąd bo odejmujemy od 2021 resztę liczb

W takich przypadkach zmuszeni jesteśmy do użycia notacji z kwadratowymi nawiasami:


calendar["2021-11-11"] //"Narodowe Święto Niepodległości"

Z podobną sytuacją już się zresztą spotykaliśmy. W przypadku odwoływania się do kluczy tablic (które też są obiektami) używaliśmy notacji z kwadratowymi nawiasami:


const tab = ["kot", "pies", "chomik ninja"];

tab.0 //błąd
tab[0] //"kot"
tab["0"] //"kot"

tab.2 //błąd
tab[2] //"chomik ninja"
tab["2"] //"chomik ninja"

tab.2.length //błędne odwołanie
tab["2"].length //12
tab["2"]["length"] //12

tab.length //3 - bo length to właściwość o nazwie "length"
tab["length"] //3

Poza powyższymi dwoma sposobami, do wyciągania danych z obiektów możemy użyć tak zwanego przypisania destrukturyzacji. Zajmiemy się nim w jednym z kolejnych rozdziałów.


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

//zamiast
const name = obj.name;
const surname = obj["surname"];
const age = obj.age;

//mogę
const {name, surname, age} = obj;

const tab = ["Ala", "Ola", "Ela"];

//zamiast
const name1 = tab[0];
const name2 = tab[1];

//mogę
const [name1, name2] = tab;

Dodawanie nowych właściwości

Do stworzonego wcześniej obiektu możemy dodawać metody i właściwości nie tylko w jego ciele podczas tworzenia (jak powyżej), ale także poza nim:


const dog = {
    name: "Szama",
    speed: 1000,
    showText() {
        return "Lubię walczyć ze złem";
    }
}

dog.type = "pies";
dog.legs = 4;
dog.eat = function() {
    return "Jem dobre rzeczy";
}

dog.eat();
dog.showText();

W nowych wersjach Javascript (od ECMAScript 2015) do zapisu kluczy wewnątrz obiektu możemy też użyć notacji z kwadratowymi nawiasami. W poprzednich wersjach Javascript, gdy chcieliśmy dodać nową właściwość korzystając ze składni nawiasów kwadratowych, musieliśmy robić to poza obiektem. Teraz nie jesteśmy już tak ograniczeni:


//ES5
const obj = {
    name: "Karol",
    surname: "Nowak"
}
obj["my pet"] = "Pies";
obj["eat food"] = function() {
    return "Lubię jeść jabłka";
}

//ES6
const obj = {
    name: "Karol",
    surname: "Nowak"
    ["my pet"] : "Pies",
    ["eat food"]() {
        return "Lubię jeść jabłka";
    }
}

obj["my pet"] //"Pies"
obj["eat food"]() //"Lubię jeść jabłka"

Nie jest to rzecz, którą będziesz używać zbyt często. Przydaje się raczej przy bardziej zaawansowanym programowaniu, gdzie używa się Symboli.

Obiekty w obiektach

Powyżej tworzone obiekty to bardzo proste byty.

Obiekty mogą być bardziej rozbudowane, bo każda właściwość może wskazywać na kolejne obiekty, a te z kolei na kolejne. I tak najczęściej jest. Im dalej w las tym będziesz się stykać z coraz bardziej rozbudowanymi tworami:


const person = {
    name: "Marcin",

    pet: {
        name: "Szama",
        color: "brown",
        speed: 1000,

        collar: {
            color: "red",
            length: "25cm"
        },

        favoriteFood: ["mięso", "mięso", "mięso"]
    }
}

person.name //"Marcin"
person.pet.name //"Szama"
person.pet.collar.color //"red"
person.pet.favoriteFood[1] //"mięso"

W ramach testu wpisz w konsoli debugera nazwy popularnych obiektów: window, console, Date, po każdym naciśnij enter i zbadaj każdy z nich rozwijając jego zawartość.

this

Aby odwołać się do danego obiektu z wnętrza którejś z jego metod możemy tak jak powyżej użyć nazwy obiektu:


const car = {
    brand: "Mercedes",
    color: "czerwony",
    showText() {
        console.log(`${car.brand} koloru ${car.color}`);
    }
}

car.showText(); //"Mercedes koloru czerwony"

Nie jest to jednak zalecana metoda. O wiele lepiej jest w tym przypadku skorzystać ze słowa kluczowego this, które wskazuje na dany obiekt:


const car = {
    brand: "Mercedes",
    color: "czerwony",
    showText() {
        console.log(`${this.brand} koloru ${this.color}`);
    }
}

car.drive = function() {
    console.log(this.brand + " - jadę!");
}

car.showText(); //"Mercedes koloru czerwony"
car.drive(); //"Mercedes - jadę!"

Ma to o tyle znaczenie, ponieważ dzięki takiemu podejściu, mamy większe możliwości - ot chociażby pojedynczą funkcję możemy zastosować w kilku obiektach:


function show() {
    console.log(`${this.brand} koloru ${this.color}`);
}

const car1 = {
    brand: "Mercedes",
    color: "czerwony",
    showText: show
}

const car2 = {
    brand: "BMW",
    color: "czarny",
    showText: show
}

car1.showText(); //"Mercedes koloru czerwony"
car2.showText(); //"BMW koloru czarny"

W powyższym kodzie dla metody showText() użyliśmy referencji do funkcji show() - czyli poprzez nazwę wskazaliśmy na właściwą funkcję. Używanie referencji jest charakterystyczną cechą dla obiektów, a w Javascript funkcje też są obiektami, mającymi swoje właściwości i metody. Aby je zbadać, wystarczy w konsoli wpisać console.dir(nazwaFunkcji).

Więcej na temat this dowiesz się w rozdziale o this. Na chwilę obecną zapamiętaj tylko, że do danego obiektu z wnętrza jego funkcji odwołujemy się za pomocą this.

Usuwanie właściwości i metod

Aby usunąć właściwość lub metodę obiektu, skorzystamy ze słowa delete:


const car = {
    brand: "Mercedes",
    color: "czerwony",
    showText() {
        console.log(`${this.brand} koloru ${this.color}`);
    }
}

console.log(car.color); //czerwony
delete car.color;
console.log(car.color); //undefined

W JavaScript nie musimy usuwać całych obiektów. Jeżeli na dany obiekt nie będzie wskazywała żadna zmienna (czyli dany obiekt nie będzie w żaden sposób dostępny), jego automatycznym usunięciem zajmie się tak zwany Garbage Collection. Jeżeli chciałbyś usunąć cały obiekt, nie musisz tego robić. Wystarczy, że zmienną wskazującą na dany obiekt ustawisz na null, co zerwie referencje. W większości przypadków jednak nie musisz się tym przejmować, bo Javascript dba o to za ciebie.

Pętla po obiekcie

Jeżeli chcemy zrobić pętlę po kluczach obiektu, najczęściej stosowaną będzie pętla for in. Jej zastosowanie ma następującą postać:


const car = {
    brand: "Mercedes",
    color: "czerwony",
    showText() { ... }
}

for (const key in car) {
    console.log(key); //brand, color, showText
}

Jak widzisz robiąc iteracje pod zmienną key podstawiane są kolejne klucze. Aby pobrać ich wartość zastosujemy kwadratowe nawiasy:


for (const key in car) {
    console.log("Klucz: ", key);
    console.log("Wartość: ", car[key]);
}

Nie jest to niestety idealne rozwiązanie. Jak się dowiesz z dalszej części tego rozdziału, obiekty w Javascript posiadają prototypy, które są schowkiem dla wspólnych funkcjonalności danej grupy obiektów. Pętla for in robi pętlę po kluczach danego obiektu, ale także po kluczach prototypu danego obiektu.

Możesz to sprawdzić na tej stronie.

Żeby wypisać tutaj tylko klucze danego obiektu, musimy przeprowadzić dodatkowe sprawdzenie za pomocą funkcji hasOwnProperty():


const car = {
    brand: "Mercedes",
    color: "czerwony",
    showText() { ... }
}

for (const key in car) {
    if (car.hasOwnProperty(key)) {
        console.log(key);
    }
}

W dzisiejszych czasach możemy też zastosować dodatkowe funkcje, które zwracają tablice z odpowiednimi danymi (kluczami, wartościami i parami klucz-wartość). W przeciwieństwie do pętli for in, zwracane są dane tylko z konkretnego obiektu - z pominięciem jego prototypu.

Więcej na ten temat dowiesz się w rozdziale o iteratorach.

Trening czyni mistrza

Jeżeli chcesz sobie potrenować zdobytą wiedzę z tego działu, to zadania znajdują się w repozytorium pod adresem: https://github.com/kartofelek007/zadania-obiekty

W repozytorium jest branch "solutions". Tam znajdziesz przykładowe rozwiązania.

Wszelkie prawa zastrzeżone. Jeżeli chcesz używać jakiejś części tego kursu, skontaktuj się z autorem. Aha - i ta strona korzysta z ciasteczek.

Menu