Úvod
Když přemýšlíte o třídách a Objektově orientované programování jako paradigma není JavaScript pravděpodobně prvním jazykem, který vás napadne.
V této příručce se pokusíme posunout JavaScript dále v seznamu přidružení a probereme, jak aplikovat Objektově orientované principy při psaní kódu JavaScript. Stojí za zmínku, že některé funkce, kterými se budeme zabývat, jsou stále ve vývoji, ale většina je ve výrobě a plně funkční. Průvodce náležitě aktualizujeme, jakmile bude vydán.
Vzhledem k tomu, že JavaScript se většinou používá na webu, aplikace OOP na něj může být opravdu užitečná, řekněme, když získáváte data ze serveru (například kolekce z databáze MongoDB), kterou můžete vytvořit ve třídě s atributy, protože dělá práci s daty intuitivnější a jednodušší.
Co je objektově orientované programování (OOP)?
Než začneme, proberme si definici OOP a některé základní principy. Pokud jste již obeznámeni s těmito koncepty, můžete pokračovat a přeskočit k vytvoření třídy v JavaScriptu.
Třída a atributy
Řekněme, že máme opravdu jednoduchou třídu s názvem 03
který má dva atributy - 18
a 29
, což jsou oba struny. Toto je náš plán na výrobu předmětu. Objekt této třídy by měl atributy a hodnoty, řekněme 38
a 41
.
Abychom mohli vytvářet objekty jako je tento z konkrétní třídy, musí tato třída obsahovat metodu konstruktoru - nebo zkráceně konstruktér . Konstruktor je prakticky řečeno manuál o tom, jak vytvořit instanci objektu a přiřadit hodnoty . Nejběžnější praxí při vytváření konstruktoru je pojmenovat jej stejně jako třídu, ale nemusí to tak být.
Například pro naše 51
třídu, definovali bychom 64
konstruktor, který definuje jak atributům v rámci třídy přiřazujeme hodnoty při vytváření instance. Obvykle přijímá 78
argumenty používané jako hodnoty pro atributy:
class ProgrammingLanguage {
// Attributes
String name;
String founder;
// Constructor method
ProgrammingLanguage(string passedName, string passedFounder){
name = passedName;
founder = passedFounder;
}
}
Poznámka: I když je to podobné, nejedná se o kód JavaScript a slouží pro ilustrativní účely. Při vytváření třídy budeme používat JavaScript.
Poté, když vytváříme instanci této třídy, předáme některé argumenty konstruktoru, vyvoláním 89
objekt:
ProgrammingLanguage js = new ProgrammingLanguage("JavaScript", "Brendan Eich");
To by vytvořilo objekt js typu 90
s atributy 102
a 111
.
Metody getter a setter
V OOP je další sada klíčových metod – getters a setři . Jak název napovídá, getter metoda získává nějaké hodnoty, zatímco setter nastaví je.
V OOP se používají k načítání atributů z objektu, spíše než k přímému přístupu k nim, k jejich zapouzdření, provádění potenciálních kontrol atd. Setters se používají k nastavení atributů objektů na dané hodnoty – opět v zapouzdřeném a izolovaném způsobem.
Poznámka: Aby byl tento přístup skutečně omezen, jsou atributy obvykle nastaveny na 127
(nepřístupné mimo třídu), když daný jazyk podporuje modifikátory přístupu.
Může vám být například zabráněno, pokud chcete někomu nastavit věk na 130
prostřednictvím nastavení , což by nebylo možné vynutit, pokud byste měli povolen přímý přístup k atributům.
Setters lze použít buď k aktualizaci hodnoty, nebo k jejímu počátečnímu nastavení, pokud použijete prázdné konstruktor – tj. konstruktor, který zpočátku nenastavuje žádné hodnoty.
Konvence pro pojmenování getterů a setterů je, že by měly mít předponu 144
nebo 157
, následovaný atributem, se kterým se zabývají:
getName() {
return name;
}
setName(newName) {
name = newName;
}
toto Klíčové slovo
Třídy jsou sebevědomé . 161
klíčové slovo se používá k odkazování na tuto instanci v rámci třídy, jakmile je vytvořena instance. Klíčové slovo budete vždy používat pouze v rámci třídy, která odkazuje sama na sebe.
Například v konstruktoru z dřívějška jsme použili předané proměnné 170
a 188
, ale co kdyby to byly jen 194
a 206
co dává větší smysl?
Náš konstruktor by vypadal takto:
ProgrammingLanguage(String name, String founder) {
name = name;
founder = founder;
}
Takže, které 216
nastavujeme na které 226
? Nastavujeme předávanou hodnotu atributu nebo naopak?
Zde je 237
klíčové slovo začíná:
ProgrammingLanguage(String name, String name) {
this.name = name;
this.founder = founder;
}
Nyní je zřejmé, že nastavujeme hodnotu atributu této třídy na hodnotu předanou z konstruktoru.
Stejná logika platí pro naše getry a nastavovače:
getName() {
return this.name;
}
setName(name) {
this.name = name;
}
Získáváme a nastavujeme název z této třídy .
Syntaxe atributů a konstruktorů a také konvence pro používání velkých písmen se jazyk od jazyka liší, ale hlavní principy OOP zůstávají stejné.
Vzhledem k tomu, jak jsou konstruktory, getry a settery standardizované, má většina dnešních IDE integrovanou zkratku pro vytvoření metody konstruktoru a také getterů a setterů. Vše, co musíte udělat, je definovat atributy a vygenerovat je pomocí příslušného zástupce ve vašem IDE.
Nyní, když jsme se více seznámili s koncepty OOP, můžeme se ponořit do OOP v JavaScriptu.
Vytvoření třídy v JavaScriptu
Poznámka: Jedním z rozdílů, které JavaScript přináší, je to, že při definování tříd – nemusíte explicitně uvádět, které atributy/pole má. Je mnohem poddajnější a objekty stejné třídy mohou mít různá pole pokud si to přeješ. Na druhou stranu, toto se nedoporučuje vzhledem k faktu, že je to v rozporu s principy OOP, a standardizovaná praxe je částečně vynucována tím, že máte konstruktor, ve kterém nastavujete všechny atributy (a tedy máte nějaký druh seznamu atributů).
V JavaScriptu existují dva způsoby, jak vytvořit třídu:pomocí deklarace třídy a pomocí výrazu třídy .
Pomocí deklarace třídy , prostřednictvím 244
klíčové slovo, můžeme definovat třídu a všechny její atributy a metody v následujících složených závorkách:
class Athlete {}
Ty mohou být definovány v příslušných souborech nebo v jiném souboru spolu s jiným kódem jako třída pohodlí.
Případně pomocí výrazů třídy (pojmenovaný nebo nepojmenovaný) vám umožňuje definovat a vytvořit je inline:
// Named
let Athelete = class Athlete{}
// Unnamed
let Athlete = class {}
// Retrieving the name attribute
console.log(Athlete.name);
Načítání atributu tímto způsobem se nedoporučuje, stejně jako ve skutečném duchu OOP – neměli bychom mít přímý přístup k atributům třídy.
Protože nemáme konstruktor, ani gettery a nastavovače, pojďme si je definovat.
Vytvoření konstruktoru, getterů a setterů v JavaScriptu
Další věc, kterou je třeba poznamenat, je, že JavaScript vynucuje jméno konstruktéra. Musí se jmenovat 252
. Toto je také místo, kde v podstatě definujete atributy své třídy, i když trochu implicitněji než v jazycích, jako je Java:
class Athlete{
constructor(name, height, weight){
this._name = name;
this._height = height;
this._weight = weight;
}
}
const athlete = new Athlete("Michael Jordan", 198, 98);
Pokud byste chtěli definovat atributy předem, můžete ale je to nadbytečné vzhledem k povaze JavaScriptu, pokud se nepokoušíte vytvořit soukromé vlastnosti. V každém případě byste měli před názvy atributů uvést 264
.
Vzhledem k tomu, že JavaScript nepodporoval zapouzdření hned po vybalení, byl to způsob, jak uživatelům vaší třídy říci, neby přistupovat k atributům přímo. Pokud někdy před názvem atributu uvidíte podtržítko - udělejte sobě a tvůrci třídy laskavost a nepřistupujte k němu přímo.
Poznámka: Bylo to technicky možné k vytváření soukromých atributů v rámci tříd JavaScriptu, ale nebyl široce přijat ani používán - Douglas Crockford navrhl skrytí proměnných v uzávěrkách, aby se dosáhlo tohoto efektu.
Zdarma e-kniha:Git Essentials
Prohlédněte si našeho praktického průvodce učením Git s osvědčenými postupy, průmyslově uznávanými standardy a přiloženým cheat sheetem. Přestaňte používat příkazy Google Git a skutečně se naučte to!
Svůj záměr můžete dále anotovat pomocí 270
anotace označující, jakou úroveň přístupu chcete, aby atribut měl:
class Athlete {
/** @access private */
_name;
constructor(name){
this._name = name;
}
getName() {
return this._name;
}
setName(name) {
this._name = name;
}
}
Poté můžete vytvořit instanci objektu a také získat a nastavit jeho atribut:
var athlete = new Athlete('Michael Jordan');
console.log(athlete.getName());
athlete.setName('Kobe Bryant');
console.log(athlete.getName());
Výsledkem je:
Michael Jordan
Kobe Bryant
Můžete však také přistupovat přímo k nemovitosti:
console.log(athlete._name); // Michael Jordan
Nastavení polí jako soukromých
Nakonec soukromá pole byly představeny a mají předponu 282
. Ve skutečnosti vynucují použití polí jako soukromých a nemohou být přístupný mimo třídu – pouze prostřednictvím metod, které jej odhalují:
class Athlete {
/** @access private */
#name;
constructor(name){
this.#name = name;
}
getName() {
return this.#name;
}
setName(name) {
this.#name = name;
}
}
var athlete = new Athlete('Michael Jordan');
console.log(athlete.getName()); // Michael Jordan
console.log(athlete.#name); // SyntaxError: Private field '#name' must be declared in an enclosing class
Tímto způsobem je skutečně dosaženo zapouzdření, protože uživatelé mohou přistupovat k atributům pouze prostřednictvím prověřených metod, které mohou ověřit vrácené hodnoty nebo jim zabránit v nastavení neočekávaných hodnot, jako je přiřazení čísla namísto řetězce k 292 atribut.
Poznámka: Chcete-li označit atribut jako soukromý, musíte jej deklarovat před gettry a settery. Tato funkce je aktivní od roku 2018 (Babel 7.0+), ale v některých starších prostředích nemusí fungovat.
získat a nastavit Klíčová slova
Případně má JavaScript speciální sadu klíčových slov - 305
a 316
, které lze použít k výrobě getrů a setrů. Při použití se svazují určité atributy funkcí vyvolaných, když k nim chcete přistupovat.
Je obvyklé používat stejný název mezi atributem a metodami getter/setter vázanými 326
a 335
, bez prefix (bylo by to nadbytečné):
class Athlete {
constructor(name) {
this._name = name;
}
get name() {
return this._name;
}
set name(name){
this._name = name;
}
}
var athlete = new Athlete("Michael Jordan");
console.log(athlete.name); // Output: Michael Jordan
athlete.name = "Kobe Bryant";
console.log(athlete.name); // Output: Kobe Bryant
I když to tak může vypadat, nejsme přístup k 349
atribut přímo. Implicitně voláme 352
zkusením pro přístup k atributu, když je požadavek přesměrován na 361
metoda. Aby to bylo jasnější, upravme 378
tělo metody:
get name() {
return "Name: " + this._name;
}
Nyní toto:
var athlete = new Athlete('Michael Jordan')
console.log(athlete.name);
Výsledky v:
Name: Michael Jordan
Poznámka: Další důvod, proč přidat podtržítko (384
) k názvům atributů je, pokud budete tento přístup používat pro definování getterů a setterů. Pokud bychom měli použít pouze 395
jako atribut by to bylo nejednoznačné vzhledem ke skutečnosti, že 409
může také odkazovat na 414
.
Jakmile se pokusíme vytvořit instanci třídy, spustilo by to rekurzivní smyčku, která by zaplnila zásobník volání, dokud mu nedojde paměť:
class Athlete {
constructor(name) {
this.name = name;
}
get name() {
return this.name;
}
set name(name) {
this.name = name;
}
}
var athlete = new Athlete('Michael Jordan');
console.log(athlete.name);
Výsledkem je:
script.js:12
this.name = name;
^
RangeError: Maximum call stack size exceeded
Používáte funkce Getter/Setter nebo klíčová slova?
Komunita je rozdělena ve výběru mezi těmito a někteří vývojáři preferují jeden před druhým. Neexistuje žádný jasný vítěz a oba přístupy podporují principy OOP tím, že umožňují zapouzdření a mohou vracet a nastavovat soukromé atributy.
Definování metod třídy
Některé metody jsme již definovali dříve, jmenovitě metody getter a setter. V podstatě stejným způsobem můžeme definovat další metody, které provádějí jiné úkoly.
Existují dva hlavní způsoby, jak definovat metody - in-class a mimo třídu .
Doposud jsme používali definice ve třídě:
class Athlete {
// Constructor, getters, setters
sayHello(){
return "Hello, my name is " + this.name;
}
}
console.log(athlete.sayHello()) // Hello, my name is Kobe Bryant
Alternativně můžete explicitně vytvořit funkci prostřednictvím deklarace funkce mimo třídu:
class Athlete {
// Class code
}
athlete.sayHello = function(){
return "Hello, my name is " + athlete.name;
}
var athlete = new Athlete("Kobe Bryant");
console.log(athlete.sayHello()) // Output: Hello, my name is Kobe Bryant
Pokud jde o JavaScript, oba tyto přístupy jsou stejné, takže si můžete vybrat, který vám bude lépe vyhovovat.
Dědičnost tříd v JavaScriptu
Klíčovým konceptem OOP je třídní dědičnost . podtřída (dětská třída) lze rozšířit z třídy a definovat nové vlastnosti a metody při dědění některé z jeho supertřídy (rodičovská třída).
425
může být 438
, 440
nebo 454
ale všechny tři jsou instancí 466
.
V JavaScriptu 474
klíčové slovo se používá k vytvoření podtřídy:
// Athlete class definition
class BasketballPlayer extends Athlete {
constructor(name, height, weight, sport, teamName){
super(name, height, weight);
this._sport = sport;
this._teamName = teamName;
}
get sport(){
return this._sport;
}
get teamName(){
return this._teamName;
}
}
const bp = new BasketballPlayer("LeBron James", 208, 108, "Basketball", "Los Angeles Lakers");
Vytvořili jsme objekt 481
třída, která obsahuje atributy použité v 497
class a také dva nové atributy, 501
a 511
- specifické pro 526
třída.
Podobně jako 532
odkazuje na tuto třídu , 546
odkazuje na nadtřídu. Zavoláním na číslo 553
s argumenty voláme konstruktor nadtřídy a nastavujeme několik atributů, než nastavíme nové specifické pro 564
třída.
Když použijeme 572
klíčové slovo, zdědíme všechny metody a atributy, které jsou přítomné v nadtřídě - to znamená, že jsme zdědili 587
metoda, getry a settery a všechny atributy. Můžeme vytvořit novou metodu pomocí této metody a přidat k ní další, například takto:
class BasketballPlayer extends Athlete{
// ... previous code
fullIntroduction(){
return this.sayHello() + " and I play " + this.sport + " in " + this.teamName;
}
}
const bp = new BasketballPlayer("LeBron James", 208, 108, "Basketball", "Los Angeles Lakers");
console.log(bp.fullIntroduction());
Což bude mít za následek:
Hello, my name is LeBron James and I play Basketball in Los Angeles Lakers
Poznámka: Nedefinovali jsme 599
metoda v 608
třídy, ale stále k němu má přístup přes 611
. Jak to? Není to součástí 621
třída? To je. Ale 635
zdědil tuto metodu takže je to tak dobré, jak je definováno v 649
třída.
instance Operátor
654
Operátor se používá ke kontrole, zda je nějaký objekt instance určitá třída. Návratový typ je 662
:
var bp = new BasketballPlayer();
var athlete = new Athlete();
console.log(bp instanceof BasketballPlayer); // Output: true
console.log(bp instanceof Athlete); // Output: true
console.log(athlete instanceof Athlete); // Output: true
console.log(athlete instanceof BasketballPlayer); // Output: false
A 672
je 684
takže 697
je příkladem obojího. Na druhé straně 700
nemusí být 714
, tedy 722
je pouze instancí 734
. Pokud vytvoříme instanci 748
jako basketbalový hráč , například 757
, jsou instancí obou.
Závěr
V této příručce jsme se podívali na některé základní principy OOP a také na to, jak fungují třídy v JavaScriptu. JavaScript ještě není plně vhodný pro OOP, ale činí se kroky k dalšímu přizpůsobení funkčnosti.
Prozkoumali jsme definice tříd, atributy, getry, nastavovače, zapouzdření, metody tříd a dědičnost.