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 usprawnień jest wprowadzenie składni class, czyli nowego zapisu na istniejący oparty o prototypy model dziedziczenia. Składnia ta nie zmienia starego mechanizmu, a jest tylko lukrem składniowym (ang. sugar code), czyli przyjemniejszym zapisem tego samego.
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ć.

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 (tak jak np. poniżej), 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;
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
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.