Class

Gdybyśmy popatrzyli na wiele zorientowanych obiektowo języków programowania, zauważylibyśmy, że w wielu z nich obiekty tworzone są na bazie tak zwanych klas, które są swoistą templatką, na bazie której później tworzymy indywidualne instancje/egzemplarze. Każda taka klasa dość często posiada funkcję constructor (przy czym może się ciut różnić jej nazwa) oraz destructor. Ta pierwsza odpalana jest w momencie tworzenia nowej instancji i najczęściej służy do ustawiania podstawowych właściwości, ale i automatycznego odpalania odpowiednich metod. Destructor - przeciwnie - odpalany jest przy usuwaniu danej instancji i służy do czyszczenia wszelkich śmieci, które mogą potencjalnie walać się w pamięci.

Wraz z rozwojem języka Javascript wiele mechanizmów usprawniono, a korzystanie z nich zostało ułatwione. Jednym z takich ułatwień jest wprowadzenie składni class, a w raz z nią kilku dodatkowych usprawnień i możliwości.

Deklaracja klasy

Ogólna deklaracja klasy ma postać:


class Animal {
    constructor() { }
    method1() { }
    method2() { }
    method3() { }
}

//lub o wiele rzadziej spotykane

const Animal = class {
    constructor() { }
    method1() { }
    method2() { }
    method3() { }
}

//tworzymy instancje tak samo jak poprzednio
const pet1 = new Animal();
const pet2 = new Animal();

Powiedzieliśmy sobie powyżej, że składnia class jest tylko innym zapisem tego co robiliśmy poprzednio za pomocą konstruktor function. Sprawdźmy to za pomocą kodu:


class AnimalA {}
console.log(typeof AnimalA); //function

function AnimalB() {}
console.log(typeof AnimalB); //function

constructor()

Każda klasa posiada specjalną funkcję constructor(), która jest automatycznie odpalana przy tworzeniu nowej instancji za pomocą słowa new.


class Animal {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    method1() { }
    method2() { }
    method3() { }
}

const animal = new Animal("pies", 8);

Funkcja constructor() jest tym samym, co używana w poprzednim zapisie funkcja będąca konstruktorem:


function PersonFn(name, age) {
    this.name = name;
    this.age = age;
}

//w nowej składni ES6

class PersonCl {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
}

Żeby stworzyć nową instancję na bazie takiej klasy użyjemy takiego samego zapisu z new co przy starszej składni.


const person1 = new PersonFn("Karol", 15);
const person2 = new PersonCl("Piotr", 18);

Tym razem jednak nie możemy pominąć new:


const person1 = PersonFn("karol", 15); //błędu nie będzie, chociaż kod będzie działał źle
const person2 = PersonCl("Piotr", 18); //błąd - Class constructor Test cannot be invoked without 'new'

Dodawanie metod

Dodając nowe metody i właściwości do prototypu danej klasy w nowej składni nie musimy już pamiętać o składni prototype. Wystarczy, że metody te umieścimy wewnątrz klasy:


class Animal {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    eat() {
        console.log(this.name + " jem");
    }

    sleep() {
        console.log(this.name + " śpię");
    }
}

Tak jak w przypadku starszego zapisu, metody takie automatycznie trafią do prototypu danych obiektów, dzięki czemu wszystkie obiekty budowane na bazie danej klasy będą mogły z nich korzystać.

Class metody

W przeciwieństwie do starszego zapisu metody takie nie będą listowane, jeżeli po danym obiekcie zrobimy pętlę for in, a i w przypadku klas każda metoda ma swoją właściwość name, co czasami może się przydać. Rzeczy te możesz sprawdzić na przykładowej stronie.

Właściwości

Właściwości dla danej instancji dodajemy wewnątrz konstruktora:


class Animal {
    constructor(name) {
        this.legs = 4;
        this.type = "animal";
    }
}

Zapis ten w najnowszej wersji JavaScript został dodatkowo uproszczony, dzięki czemu możemy je definiować także poza konstruktorem:


//równoznaczne z powyższym kodem
class Animal {
    legs = 4;
    type = "animal";
}

class Animal {
    legs = 4;
    type = "animal";

    constructor(name) {
        this.name = name;
    }
}

//jest równoznaczne jest z

class Animal {
    constructor(name) {
        this.name = name;
        this.legs = 4;
        this.type = "animal";
    }
}

Metody statyczne

Jeżeli stworzymy metody danej klasy, trafią one do prototypu obiektów tworzonych na bazie tej klasy:


class Human {
    constructor(name) {
        this.name = name;
    }

    say() {
        console.log("Jestem człowiek");
    }
}

Human.say(); //błąd, bo say jest w prototypie
Human.prototype.say(); //"Jestem człowiek"

const ob = new Human("Marcin");
ob.say(); //"Jestem człowiek"

Jak wiesz, dane metody możemy przypisywać nie tylko do prototypu obiektów, ale także bezpośrednio do instancji obiektu:


const ob = new Human("Marcin");
ob.say(); //Jestem człowiek
ob.eat = function() {
    console.log("Jem śniadanie");
}
ob.prototype.eat(); //nie ma, bo tylko powyższa instancja ma tą metodę

W JavaScript możemy też tworzyć metody statyczne, które są dostępne dla danej klasy.

Metody takie nie są dostępne dla nowych instancji, a tylko dla samych klas:


//w ES5
function Human {
    this.name = name;
}

//metoda statyczna
Human.create = function() {
   console.log("Tworzę");
}

Human.prototype.say = function() {
    console.log("Jestem człowiek");
}

const ob = new Human("Marcin");
ob.create(); //błąd
Human.create(); //"Tworzę"

//w ES6
class Human {
    constructor(name) {
        this.name = name;
    }

    say() {
        console.log("Jestem człowiek");
    }

    static create() {
        console.log("Tworzę");
    }
}

const ob = new Human("Marcin");
ob.create(); //błąd
Human.create(); //"Tworzę"

Metody statyczne najczęściej wykorzystywane są do tworzenia użytecznych funkcji dla danej klasy. Można dzięki temu pogrupować funkcjonalności dotyczące danych klas w jednym miejscu:


class User {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }

    static compareByName(a, b) {
        if (a.name  b.name) return 1;
        return 0;
    }

    static compareByAge(a, b) {
        return a.age - b.age;
    }
}

const users = [
    new User("Tomek", 10),
    new User("Ania", 35),
    new User("Beata", 20),
    new User("Monika", 20),
    new User("Karol", 22)
];

users.sort( User.compareByName );
console.log( users[0].name ); // Ania

users.sort( User.compareByAge );
console.log( users[0].name ); // Tomek

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.