JavaScript i praca z obiektami
Chciałbym zaprezentować Wam moje przemyślenia w temacie obiektów i pracy z nimi, czyli czegoś, z czym mają do czynienia programiści nie tylko w JavaScripcie, ale w większości innych języków.
Obiekty
JavaScript obraca się wokół prostego paradygmatu obiektowego. Wszyscy znamy obiekty i klasy, pracujemy z nimi prawdopodobnie przez całą naszą karierę.
Obiekt jest zbiorem właściwości, a właściwość jest atrybutem tego obiektu. Można to porównać do nas, ludzi. Mamy swoje atrybuty, może to być nasze imię, nazwisko, data urodzenia, czy wzrost i waga.
Obiekty i programowanie obiektowe są w rzeczywistości inspirowane przez obiekty w prawdziwym życiu. Poniższe przykłady to takie „how-to” dla pracy z obiektami, od prostych do bardziej zaawansowanych przypadków.
Sposoby na instancjonowanie obiektów
Zwykły sposób instancjonowania obiektów jest następujący:
# Create new object
const site = {
name: "Medium",
domain: "medium.com",
type: "Writing Platform"
}
Ale powyższy jest inicjalizatorem obiektu, ograniczoną przecinkami listą właściwości zamkniętych w nawiasach klamrowych {}
, a także jest krótszą składnią. Bardziej wymagający sposób byłby następujący.
# Create new object
const site = new Object();
site.name = "Medium";
site.domain = "medium.com";
site.type = "Writing Platform";
Do ustawienia samych właściwości można użyć nazwy klucza danej właściwości lub przypisać ją za pomocą nawiasów []
.
site['name'] = "Medium";
Korzyścią z zastosowania tego podejścia jest to, że możesz ustawić wartości właściwości za pomocą zmiennych.
const propertyName = "name";
const propertyValue = "Medium";
site[propertyName] = propertyValue;
Warto o tym wiedzieć i pamiętać, gdy pracujemy z kolekcjami, listami mapującymi, obiektami, typami itp.
Klasy
Praca z obiektami nie obędzie się bez pracy z klasami.
Zapewne wiesz już jak działają klasy, więc w skrócie — jest to szablon dla obiektów, definiujący jakie właściwości i funkcje będzie miała instancja, czyli obiekt.
# Classes
class Website {
constructor(name, domain, type){
this.name = name;
this.domain = domain;
this.type = type;
}
// Getter
get url() {
return `https://www.${this.domain}`;
}
// Method
sayHello() {
return `Hello from ${this.name}!`;
}
}
const medium = new Website("Medium",
"medium.com",
"Writing Platform");
const url = medium.url;
const greeting = medium.sayHello();
W powyższym przykładzie w constructor
zdefiniowane są trzy pola. Jest to to samo, co wcześniejsze definiowanie pól na własną rękę.
Ciekawą rzeczą jest jednak użycie getters
, które definiuje się jako funkcję get
. Nie jest przywoływalna jak zwykłe funkcje, ale zachowuje się jak właściwość.
Pola
Pola lub właściwości mogą być rzeczywiście zadeklarowane przed konstruktorem.
# Fields and Private Fields
class Website {
name;
domain;
#type;
constructor(name, domain, type){
this.name = name;
this.domain = domain;
this.#type = type;
}
}
const medium = new Website("Medium",
"medium.com",
"Writing Platform");
Zauważ, jak symbol #
może być użyty do zadeklarowania pól prywatnych. To pole nie będzie publicznie dostępne poza klasą.
Generatory i funkcja „yield”
Generatory i słowo kluczowe yield
to coś stosunkowo nowego w JavaScripcie. Pozwala nam agregować i uzyskiwać wyniki z wnętrza pętli — coś, co nie jest tak powszechne w innych językach, ale jest obecne w C# od dłuższego czasu.
# Generator and Yielding results
class Website {
constructor(tabs) {
this.tabs = tabs;
}
*getTabs() {
for(const tab of this.tabs){
yield tab;
}
}
const medium = new Website(["Home",
"Recent","About Us",
"Register"]);
const generator = medium.getTabs();
const tabs = [...generator];
Aby uzyskać wartości z generatora, musimy to rozproszyć, ponieważ sama funkcja zwraca typ, a nie wartości.
Rozszerzanie klas
W JavaScripcie klasy mogą być również rozszerzane:
# Extending Classes
class BaseWebsite {
constructor(name, domain, type){
this.name = name;
this.domain = domain;
this.type = type;
}
sayHello() {
return "Hello!";
}
}
class HttpsWebsite extends BaseWebsite {
constructor(name, domain, type){
super(name, domain, type);
this.scheme = 'https';
}
sayHello() {
let superHello = super.sayHello();
return `${superHello} from ${this.name}`;
}
}
const medium = new HttpsWebsite("Medium",
"medium.com",
"Writing Platform");
const hello = medium.sayHello();
Wykorzystując słowa kluczowe extend
i super
, jesteśmy w stanie rozszerzyć właściwości i metody istniejącej klasy.
Klasa bazowa wywołania metod
W powyższym przykładzie zauważ, że możemy również zadeklarować funkcję bazową sayHello
, nadpisać ją, ale nadal móc z niej korzystać za pomocą słowa kluczowego super
. To samo odbywa się również wewnątrz funkcji constractor
.
Domieszki
Wielokrotne dziedziczenie nie jest możliwe w JavaScripcie, tj. klasy mogą rozszerzyć się tylko do jednej superklasy. Samo dziedziczenie zachodzi w runtimie, JavaScript przeszukuje łańcuch prototypów obiektu, w celu znalezienia jego zadeklarowanych właściwości i wartości.
Możemy jednak wykorzystać domieszki. Rozważmy następujący przykład:
# Mixins
let WalkMixin = superclass => class extends superclass {
walk() {
return "I'm walking!";
}
};
let FlyMixin = superclass => class extends superclass {
fly() {
return "I'm flying!";
}
};
class BaseClass {}
class SampleClass extends WalkMixin(FlyMixin(BaseClass)) {};
const sample = new SampleClass();
console.log(sample.walk());
console.log(sample.fly());
Jeśli zbadasz instancję sample
, zauważysz, że jej łańcuch prototypów zawiera obie metody zdefiniowane w powyższych domieszkach.
Zauważ, że nie jest to funkcja JavaScriptu, ale bardziej obejście.
Mam nadzieję, że podobał ci się ten artykuł. Jest to część serii na temat zagadnień JavaScriptu, którą niedawno zacząłem tworzyć, tak więc wyczekuj kolejnych tego typu. Jeśli podoba Ci się treść tego artykułu, nie zapomnij mnie zaobserwować i dołącz do mnie w podróży, w której badam i piszę nieco więcej o nauce i inżynierii stojącej za JavaScriptem.
Oryginał tekstu w języku angielskim przeczytasz tutaj.