JavaScript używa dziedziczenia prototypowego: każdy obiekt dziedziczy właściwości i metody ze swojego prototypowego obiektu.
tradycyjna Klasa jako schemat tworzenia obiektów, używana w językach takich jak Java czy Swift, nie istnieje w JavaScript. Dziedziczenie prototypowe dotyczy tylko obiektów.
dziedziczenie prototypowe może naśladować Klasyczne dziedziczenie klas. Aby przenieść tradycyjne klasy do JavaScript, standard ES2015 wprowadza składnięclass
: cukier składniowy nad dziedziczeniem prototypowym.,
ten post zapoznaje cię z klasami JavaScript: jak zdefiniować klasę, zainicjalizować instancję, zdefiniować pola i metody, zrozumieć prywatne i publiczne pola, zrozumieć statyczne pola i metody.
1. Definicja: słowo kluczowe klasy
specjalne słowo kluczowe class
definiuje klasę w JavaScript:
class User { // The body of class}
powyższy kod definiuje klasę User
. Nawiasy klamrowe { }
ograniczają ciało klasy. Zauważ, że składnia ta nosi nazwę class declaration.,
nie jesteś zobowiązany do podawania nazwy klasy. Używając wyrażenia klasy możesz przypisać klasę do zmiennej:
const UserClass = class { // The body of class};
możesz łatwo wyeksportować klasę jako część modułu ES2015. Oto składnia domyślnego eksportu:
export default class User { // The body of class}
i nazwanego eksportu:
export class User { // The body of class}
klasa staje się użyteczna podczas tworzenia instancji klasy. Instancja jest obiektem zawierającym dane i zachowanie opisane przez klasę.,
operatornew
tworzy instancję klasy w JavaScript:instance = new Class()
.
na przykład możesz utworzyć instancję klasy User
używając operatora new
:
const myUser = new User();
new User()
tworzy instancja klasy User
.
2. Inicjalizacja: constructor()
constructor(param1, param2, ...)
jest specjalną metodą w ciele klasy, która inicjalizuje instancję., Jest to miejsce, w którym ustawia się początkowe wartości pól lub wykonuje się dowolne konfiguracje obiektów.
w poniższym przykładzie konstruktor ustawia wartość początkową pola name
:
class User { constructor(name) { this.name = name; }}
User
konstruktor ma jeden parametr name
, który służy do ustawienia wartości początkowej pola this.name
.
wewnątrz konstruktora this
wartość jest równa nowo utworzonej instancji.,
argumenty użyte do utworzenia instancji klasy stają się parametrami konstruktora:
class User { constructor(name) { name; // => 'Jon Snow' this.name = name; }}const user = new User('Jon Snow');
name
parametr wewnątrz konstruktora ma wartość'Jon Snow'
.
Jeśli nie zdefiniujesz konstruktora dla klasy, zostanie utworzony domyślny konstruktor. Konstruktor domyślny jest pustą funkcją, która nie modyfikuje instancji.
w tym samym czasie Klasa JavaScript może mieć do jednego konstruktora.
3. Pola
pola klasy są zmiennymi przechowującymi informacje., Pola mogą być dołączane do 2 encji:
- pola na instancji klasy
- pola na samej klasie (aka static)
pola mają również 2 poziomy dostępności:
- Public: pole jest dostępne w dowolnym miejscu
- Private: pole jest dostępne tylko w ciele Klasy
3.1 pola instancji publicznej
spójrzmy jeszcze raz w poprzednim fragmencie kodu:
class User { constructor(name) { this.name = name; }}
wyrażenie this.name = name
tworzy pole wystąpienia name
I przypisuje mu wartość początkową.,
później możesz uzyskać dostęp do polaname
używając dostępu do właściwości:
const user = new User('Jon Snow');user.name; // => 'Jon Snow'
name
jest polem publicznym, ponieważ możesz uzyskać do niego dostęp pozaUser
class body.
gdy pola są tworzone pośrednio wewnątrz konstruktora, tak jak w poprzednim scenariuszu, może być trudno uchwycić listę pól. Musisz je odszyfrować z kodu konstruktora.
lepszym podejściem jest jawne deklarowanie pól klasy., Bez względu na to, co zrobi konstruktor, instancja zawsze ma ten sam zestaw pól.
propozycja pól klasy pozwala zdefiniować pola wewnątrz ciała klasy. Dodatkowo możesz podać wartość początkową od razu:
class SomeClass { field1; field2 = 'Initial value'; // ...}
zmodyfikuj klasę User
i zadeklaruj publiczne pole name
:
class User { name; constructor(name) { this.name = name; }}const user = new User('Jon Snow');user.name; // => 'Jon Snow'
name;
wewnątrz ciała klasy deklaruje publiczne pole name
.,
publiczne pola zadeklarowane w ten sposób są wyraziste: wystarczy rzucić okiem na deklaracje pól, aby zrozumieć strukturę danych klasy.
Co więcej, pole class można zainicjować od razu przy deklaracji.
class User { name = 'Unknown'; constructor() { // No initialization }}const user = new User();user.name; // => 'Unknown'
name = 'Unknown'
wewnątrz ciała klasy deklaruje polename
I inicjalizuje je wartością'Unknown'
.
nie ma ograniczeń dostępu ani aktualizacji publicznych pól., Wartości można odczytywać i przypisywać do publicznych pól wewnątrz konstruktora, metod i poza klasą.
3.2 prywatne pola instancji
enkapsulacja jest ważną koncepcją, która pozwala ukryć wewnętrzne szczegóły klasy. Ktoś, kto używa zamkniętej klasy, zależy tylko od publicznego interfejsu, który zapewnia klasa i nie łączy się ze szczegółami implementacji klasy.
klasy zorganizowane z myślą o enkapsulacji są łatwiejsze do aktualizacji po zmianie szczegółów implementacji.,
dobrym sposobem na ukrycie wewnętrznych danych obiektu jest użycie pól prywatnych. Są to pola, które mogą być odczytywane i zmieniane tylko w obrębie klasy, do której należą. Świat zewnętrzny klasy nie może zmieniać pól prywatnych bezpośrednio.
prywatne pola są dostępne tylko w treści klasy.
przedrostek Nazwa pola ze specjalnym symbolem#
aby uczynić go prywatnym, np.#myField
., Prefiks #
musi być przechowywany za każdym razem, gdy pracujesz z polem: declare it, read it, or modify it.
upewnijmy się, że pole #name
może być ustawione raz przy inicjalizacji instancji:
#name
jest polem prywatnym. Możesz uzyskać dostęp i modyfikować #name
w treści User
. Metoda getName()
(więcej o metodach w następnej sekcji) może uzyskać dostęp do pola prywatnego #name
.,
ale jeśli spróbujesz uzyskać dostęp do prywatnego pola #name
pozaUser
ciało klasy, zostanie wyświetlony błąd składni: SyntaxError: Private field '#name' must be declared in an enclosing class
.
3.3 publiczne pola statyczne
Możesz również zdefiniować pola w samej klasie: pola statyczne. Są one pomocne do definiowania stałych klas lub przechowywania informacji specyficznych dla klasy.
aby utworzyć statyczne pola w klasie JavaScript, użyj specjalnego słowa kluczowego static
, a następnie nazwy pola: static myStaticField
.,
dodajmy nowe poletype
, które wskazuje typ użytkownika: admin lub regular. Pola statyczne TYPE_ADMIN
I TYPE_REGULAR
są przydatnymi stałymi do rozróżniania typów użytkowników:
static TYPE_ADMIN
I static TYPE_REGULAR
definiują statyczne zmienne wewnątrz User
class. Aby uzyskać dostęp do pól statycznych, musisz użyć klasy, po której następuje nazwa pola: User.TYPE_ADMIN
I User.TYPE_REGULAR
.
3.,4 prywatne pola statyczne
czasami nawet pola statyczne są szczegółami implementacji, które chcesz ukryć. W związku z tym można uczynić pola statyczne prywatnymi.
aby pole statyczne stało się prywatne, przedrostek nazwy pola oznaczający #
symbol specjalny: static #myPrivateStaticField
.
Załóżmy, że chcesz ograniczyć liczbę instancji klasyUser
., Aby ukryć szczegóły dotyczące limitów instancji, możesz utworzyć prywatne pola statyczne:
pole statyczne User.#MAX_INSTANCES
ustawia maksymalną liczbę dozwolonych wystąpień, podczas gdy User.#instances
pole statyczne zlicza rzeczywistą liczbę wystąpień.
te prywatne pola statyczne są dostępne tylko w klasieUser
. Nic ze świata zewnętrznego nie może zakłócać mechanizmu ograniczeń: taka jest korzyść z enkapsulacji.
4. Metody
pola przechowują dane., Ale możliwość modyfikowania danych jest wykonywana przez specjalne funkcje, które są częścią klasy: metody.
klasy JavaScript obsługują zarówno metody instancyjne, jak i statyczne.
4.1 metody instancji
metody instancji mogą uzyskiwać dostęp i modyfikować dane instancji. Metody instancji mogą wywoływać inne metody instancji, jak również dowolne metody statyczne.,
na przykład zdefiniujmy metodę getName()
która zwraca nazwę w User
class:
w metodzie klasowej, a także w konstruktorze, this
wartość jest równa instancji klasy. Użyj this
, aby uzyskać dostęp do danych instancji: this.field
, lub nawet wywołaj inne metody: this.method()
.,
dodajmy nową metodęnameContains(str)
która ma jeden parametr i wywołuje inną metodę:
nameContains(str) { ... }
jest metodąUser
klasa, która akceptuje jeden parametr str
. Co więcej, wykonuje inną metodę instancji this.getName()
, aby uzyskać nazwę użytkownika.
metoda może być również prywatna. Aby uczynić metodę prywatną prefiksem jej nazwy z #
.
zróbmygetName()
metoda prywatna:
#getName()
jest metodą prywatną., Wewnątrz metody nameContains(str)
wywołujesz prywatną metodę w taki sposób: this.#getName()
.
będąc prywatną,#getName()
nie może być wywołana pozaUser
ciało klasy.
4.2 Gettery i settery
getter i setter naśladują zwykłe pole, ale mają większą kontrolę nad tym, jak jest dostępne i zmieniane pole.
getter jest wykonywany przy próbie uzyskania wartości pola, podczas gdy setter przy próbie ustawienia wartości.,
aby upewnić się, że name
właściwość User
nie może być pusta, zawińmy prywatne pole #nameValue
w getterze i setterze:
get name() {...}
Getter jest wykonywany po uzyskaniu dostępu do wartości pola: user.name
.
4.3 metody statyczne
metody statyczne są funkcjami dołączonymi bezpośrednio do klasy. Trzymają logikę związaną z klasą, A nie z instancją klasy.,
aby utworzyć statyczną metodę, użyj specjalnego słowa kluczowego static
, a następnie standardowej składni metody: static myStaticMethod() { ... }
.
podczas pracy z metodami statycznymi należy pamiętać o dwóch prostych regułach:
- statyczna metoda może uzyskać dostęp do pól statycznych
- statyczna metoda nie może uzyskać dostępu do pól instancji.
na przykład, stwórzmy statyczną metodę, która wykrywa, czy użytkownik o określonej nazwie został już wzięty.,
isNameTaken()
jest metodą statyczną, która używa statycznego pola prywatnegoUser.#takenNames
do sprawdzania pobranych nazw.
5. Dziedziczenie: rozszerza
klasy w JavaScript obsługują pojedyncze dziedziczenie za pomocą słowa kluczowego extends
.
w wyrażeniuclass Child extends Parent { }
Child
klasa dziedziczy zParent
konstruktor, pola i metody.
na przykład utwórzmy nową klasę potomną ContentWriter
, która rozszerza klasę nadrzędnąUser
.,
ContentWriter
dziedziczy zUser
konstruktora, metodygetName()
I polaname
. Ponadto KlasaContentWriter
deklaruje nowe poleposts
.
zauważ, że prywatne Członkowie klasy rodzica nie są dziedziczone przez klasę potomną.
5.1 Konstruktor rodzica: super() w konstruktorze ()
Jeśli chcesz wywołać konstruktor rodzica w klasie potomnej, musisz użyć specjalnej funkcjisuper()
dostępnej w konstruktorze potomnym.,
na przykład zróbmy ContentWriter
konstruktor wywołujący konstruktor nadrzędny User
, a także zainicjalizujmy pole postów:
super(name)
wewnątrz klasy potomnej ContentWriter
div>wykonuje konstruktor klasy nadrzędnej User
.
zauważ, że wewnątrz konstruktora podrzędnego musisz wykonać super()
przed użyciem słowa kluczowegothis
. Wywołaniesuper()
upewnia się, że konstruktor nadrzędny inicjalizuje instancję.,
class Child extends Parent { constructor(value1, value2) { // Does not work! this.prop2 = value2; super(value1); }}
5.2 instancja rodzica: super in methods
Jeśli chcesz uzyskać dostęp do metody rodzica wewnątrz metody potomnej, możesz użyć specjalnego skrótusuper
.
getName()
klasy potomnejContentWriter
uzyskuje dostęp do metodysuper.getName()
bezpośrednio z klasy nadrzędnejUser
.
Ta funkcja jest nazywana nadpisywaniem metod.
zauważ, że możesz użyćsuper
również ze statycznymi metodami, aby uzyskać dostęp do statycznych metod rodzica.
6., Object type checking: instanceof
object instanceof Class
is the operator that determines if object
is an instance of Class
.
Let’s see instanceof
operator in action:
user
is an instance of User
class, user instanceof User
evaluates to true
.
The empty object {}
is not an instance of User
, correspondingly obj instanceof User
is false
.,
instanceof
jest polimorficzny: operator wykrywa dziecko jako instancję klasy nadrzędnej.
writer
jest instancją klasy potomnejContentWriter
. Operator writer instanceof ContentWriter
ocenia na true
.
jednocześnieContentWriter
jest klasą potomnąUser
. Tak więc writer instanceof User
ocenia do true
.
Co zrobić, jeśli chcesz określić dokładną klasę instancji?, Możesz użyć właściwości constructor
I porównać bezpośrednio z klasą:
writer.constructor === ContentWriter; // => truewriter.constructor === User; // => false
7. Klasy i prototypy
muszę powiedzieć, że składnia klasy w JavaScript robi świetną robotę, aby oderwać się od dziedziczenia prototypów. Aby opisać składnię class
nie użyłem nawet terminu prototype.
ale klasy są zbudowane na dziedziczeniu prototypowym. Każda klasa jest funkcją i tworzy instancję, gdy jest wywoływana jako konstruktor.
poniższe dwa fragmenty kodu są równoważne.,
wersja klasy:
Wersja używająca prototype:
składnia klasy jest o wiele łatwiejsza w obsłudze, jeśli znasz klasyczny mechanizm dziedziczenia języków Java lub Swift.
tak czy inaczej, nawet jeśli używasz składni klas w JavaScript, polecam Ci dobrą znajomość dziedziczenia prototypowego.
8. Dostępność funkcji klasowych
funkcje klasowe przedstawione w tym poście są rozłożone na ES2015 i propozycje na etapie 3.,
pod koniec 2019 r.funkcje klas są podzielone między:
- pola instancji publicznych i prywatnych są częścią propozycji pól klasowych
- metody instancji prywatnych i Accesory są częścią propozycji metod prywatnych Klasy
- publiczne i prywatne pola statyczne i prywatne metody statyczne są częścią propozycji właściwości statycznych Klasy
- reszta jest częścią standardu ES2015.
9. Wnioski
klasy JavaScript inicjują instancje za pomocą konstruktorów, definiują pola i metody., Możesz dołączać pola i metody nawet na samej klasie, używając słowa kluczowego static
.
dziedziczenie odbywa się za pomocą słowa kluczowegoextends
: możesz łatwo utworzyć klasę potomną z rodzica. super
słowo kluczowe jest używane do uzyskania dostępu do klasy rodzica z klasy potomnej.
aby skorzystać z enkapsulacji, Ustaw pola i metody jako prywatne, aby ukryć wewnętrzne szczegóły swoich klas. Nazwy prywatnych pól i metod muszą zaczynać się od #
.,
klasy w JavaScript stają się coraz wygodniejsze w użyciu.
Co sądzisz o używaniu#
do prefiksu własności prywatnych?