Stosowanie technik programowania obiektowego jest jednym z podstawowych paradygmatów języka JavaScript. Aby sprawnie się nimi posługiwać i tworzyć jak najlepsze aplikacje, warto przyswoić sobie odrobinę wiedzy na temat obiektów w JavaScript, gdyż prawie wszystko w tym języku jest traktowane jak obiekt. Nawet podstawowe z założenia typy danych, takie jak liczby czy łańcuchy znaków dostępne są w postaci obiektów.
Dzięki połączeniu technik programowania obiektowego oraz funkcyjnego, nasze aplikacje będą bardziej elastyczne, a kod bardziej czytelny i łatwiejszy w rozbudowie i utrzymaniu. Można powiedzieć, że JavaScript jest dosyć specyficznym językiem, jeśli chodzi o podejście do programowania obiektowego, dlatego ważne jest, aby dobrze zrozumieć mechanizmy leżące u podstaw jego działania, aby efektywnie korzystać z możliwości, które nam oferuje. Na początek zobaczmy jakie sposoby tworzenia obiektów dostępne są w JavaScript.
Wprowadzenie do konstruktorów i notacji literałowej pomoże nam lepiej zrozumieć, jak można efektywnie tworzyć obiekty w JavaScript. Przeanalizujemy różnice między tymi dwoma technikami, abyś mógł odkryć, który sposób najlepiej odpowiada Twoim potrzebom. Czytając dalej, dowiesz się więcej o różnych sposobach tworzenia obiektów w JavaScript i ich zastosowaniach.
Notacja literałowa
Literały obiektowe są jednym z najprostszych sposobów na stworzenie obiektu w języku JavaScript. Zazwyczaj obiekt reprezentuje jakiś byt. Niech to będzie koszyk, jak w poniższym przykładzie:
const cart = {
cartItems: [],
addItem(product) {
// implementacja metody
}
};
Powyżej zdefiniowaliśmy obiekt cart
z własnością cartItems
. W stałej cart
, zapisaliśmy referencję do instancji koszyka. Tworzenie własności obiektu przy użyciu notacji literałowej polega na tworzeniu par indeks – wartość, zamkniętych między nawiasami klamrowymi. Do własności możemy przypisać dowolną wartość, a także inny obiekt. Z kolei metody obiektu reprezentują czynności, które dany obiekt może wykonywać.
Własności tak utworzonego obiektu możemy usuwać w trakcie działania programu:
delete cart.cartItems;
Konstruktory obiektów
W języku JavaScript, gdy potrzebujemy tworzyć wiele obiektów o identycznej strukturze, notacja literałowa może okazać się niewystarczająca. Powtarzanie kodu może prowadzić do trudności w utrzymaniu i rozbudowie aplikacji. Na szczęście, w takich sytuacjach możemy skorzystać z konstruktorów – funkcji specjalnie zaprojektowanych do tworzenia wielu obiektów o tej samej strukturze. Konstruktor pozwala nam uniknąć powielania kodu i umożliwia elastyczne zarządzanie obiektami.
Konstruktor jest funkcją, która wywołana z operatorem new
pozwala na tworzenie wielu obiektów o tej samej strukturze. Utwórzmy sobie przykładowy konstruktor User
:
function User(id, name, email) {
this.id = id;
this.name = name;
this.email = email;
}
Aby utworzyć obiekt, należy go zainicjalizować. Jak już pisałem, do tego celu wykorzystuje się słowo kluczowe new
:
const john = new User(1, "John", "john@doe.com");
console.log(john.name); // "John"
Jak widać w powyższym przykładzie do konstruktora przekazujemy argumenty, które wykorzystywane są do inicjalizacji obiektu. Zauważmy, że tak utworzony obiekt możemy modyfikować, przykładowo dodając do niego funkcję:
john.greets = function() {
console.log(`Hello ${this.name}`);
};
Wykorzystanie konstruktorów w JavaScript otwiera drzwi do efektywnego tworzenia wielu obiektów o identycznej strukturze. Dzięki nim możemy uniknąć powielania kodu i utrzymać czytelność oraz łatwość rozbudowy naszych aplikacji. Warto zrozumieć mechanizm działania konstruktorów i wykorzystać go w odpowiednich przypadkach, aby maksymalnie wykorzystać potencjał języka JavaScript. Dzięki konstruktorom możemy skutecznie tworzyć i zarządzać obiektami, dostosowując je do naszych potrzeb i wymagań programistycznych.
Konstruktor Object()
Ten dostarczany domyślnie przez JavaScript typ konstruktora był już przez nas wykorzystywany. Kiedy tworzyliśmy obiekt przy użyciu notacji literałowej, JavaScript niejawnie wywołał konstruktor Object()
w celu utworzenia nowego obiektu. Wróćmy do poprzedniego przykładu koszyka i przekonajmy się o tym:
const cart = {};
console.log(cart.constructor == Object); // true
Technicznie rzecz biorąc, utworzenie obiektu w ten sposób w zasadzie nie różni się od użycia literału obiektowego.
const cart = new Object();
cart.cartItems = [];
Konstruktor vs Notacja Literałowa
Konstruktor w JavaScript może być bardziej efektywny niż notacja literałowa w różnych sytuacjach. Oto kilka przykładów, w których warto rozważyć użycie konstruktora:
- Tworzenie wielu obiektów o identycznej strukturze: Jeśli masz potrzebę tworzenia wielu obiektów o tej samej strukturze, konstruktor staje się wydajniejszym rozwiązaniem. Możesz zdefiniować konstruktor raz, a następnie tworzyć wiele instancji obiektów, przekazując różne wartości argumentów. To pozwala na bardziej efektywne i czytelne zarządzanie kodem.
- Inicjalizacja obiektów na podstawie zewnętrznych danych: Jeżeli chcesz tworzyć obiekty na podstawie danych pobranych z zewnętrznych źródeł, konstruktor staje się bardziej elastycznym rozwiązaniem. Możesz przekazać te dane jako argumenty konstruktora i wykorzystać je do inicjalizacji właściwości obiektu. To ułatwia tworzenie obiektów na podstawie zmiennych warunkowych lub dynamicznie zmieniających się danych.
- Dziedziczenie i rozszerzanie funkcjonalności: Konstruktor umożliwia dziedziczenie i rozszerzanie funkcjonalności obiektów poprzez manipulację prototypem. Możesz tworzyć hierarchie obiektów, które dziedziczą wspólne właściwości i metody. Dzięki temu tworzenie i zarządzanie kodem staje się bardziej modularne i łatwiejsze w utrzymaniu.
- Praca z bibliotekami i frameworkami: Wielu popularnych bibliotekach i frameworkach JavaScript, takich jak React czy Angular, konstruktor jest powszechnie używany do tworzenia instancji komponentów i usług. Te narzędzia wymagają użycia konstruktora, co sprawia, że jest on wygodnym i zalecanym rozwiązaniem.
Konstruktor i notacja literałowa są dwoma głównymi sposobami tworzenia obiektów w JavaScript. Oba mają swoje zalety i w niektórych przypadkach notacja literałowa może być bardziej odpowiednia. Ważne jest zrozumienie różnic między nimi i wybór odpowiedniego podejścia w zależności od potrzeb projektu.
Prototypy obiektów
Prototypy są jednym z najciekawszych elementów w języku JavaScript. Zobaczmy co dają nam prototypy na prostym przykładzie:
function User(id, name, email) {
// własności
this.greets = function() {
console.log(`Hello ${this.name}`);
};
}
const john = new User(1, "John", "john@doe.com");
john.greets(); // Hello John
const bob = new User(2, "Bob", "bob@example.com");
bob.greets(); // Hello Bob
Problem z powyższym przykładem polega na tym, że jest to mało wydajny sposób tworzenia obiektów. Każda nowa instancja utworzona za pomocą konstruktora User
będzie posiadała własną metodę greets()
.
Bardziej efektywne będzie w tym przypadku wykorzystanie mechanizmu prototypowania:
function User(id, name, email) {
// własności
}
User.prototype.greets = function() {
console.log(`Hello ${this.name}`);
}
Jak widać do obiektu prototypu konstruktora (User.prototype
) przypisaliśmy metodę greets()
. Powyższy zapis sprawia, że wszystkie obiekty utworzone za pomocą konstruktora User()
będą miały dostęp do metody greets(). Innymi słowy prototyp jest własnością konstruktora, w którym definiujemy elementy, które mają być dziedziczone.
Object.create()
Obiekty możemy tworzyć również wykorzystując metodę Object.create()
. Ta metoda oprócz utworzenia nowego obiektu, pozwala na ustawienie prototypu tworzonego obiektu.
const bob = Object.create(john);
W powyższym przykładzie utworzyliśmy nowy obiekt bob
z obiektu john
użytego jako prototyp. W ten sposób obiekt bob
odziedziczył zarówno własności jak i metody obiektu john
. Jak widać w metodzie tej nie ma potrzeby stosowania konstruktora.
Klasy
Standard ES6, wprowadza jeszcze jeden sposób tworzenia obiektów. Są nim klasy. Zobaczmy jak wygląda przykładowa definicja:
class User {
constructor(id, name, email) {
this.id = id;
this.name = name;
this.email = email;
}
greets() {
// kod metody
}
}
Klasa User jest tak naprawdę funkcją, a sama koncepcja klas została wprowadzona w celu uproszczenia posługiwania się konstruktorami, prototypami i dziedziczeniem. Obiekty tworzy się w sposób identyczny jak w przypadku konstruktorów:
const john = new User(1, "John", "john@doe.com");
Warto zaznaczyć, że wszystkie metody zawarte w definicji klasy, są związane z jej prototypem. Dzięki temu metody klasy nie są powielane w nowo tworzonych obiektach.
Powyższe metody umożliwiają tworzenie obiektów w JavaScript. Wybór odpowiedniej metody zależy od konkretnej sytuacji i preferencji programisty.