Prototypová dědičnost JavaScriptu a co o tom třídy ES6 říkají

Mnoho lidí přichází k JavaScriptu z jiných objektově orientovaných programovacích jazyků, jako je Java nebo C++, a jsou zmatení. "Kde jsou třídy?" JavaScript nemá třídy. JavaScript spíše používá prototypovou dědičnost k vytvoření něčeho podobného jako třídy. I když je to trochu podobné, je to stále dost odlišné a vyžaduje hodně práce, abyste je pochopili. To je účelem tříd ES6.

Nedávno jsem si prošel, jak objekty a prototypová dědičnost funguje v JavaScriptu, takže se zde o tom nebudu příliš rozepisovat, ale poukážu na řadu upozornění na to, jak se věci dnes v JavaScriptu dělají.

Aktuální prototypová dědičnost

Prototypální dědičnost není příliš obtížné začít používat pro jednoduché věci, ale je stále obtížnější (a obtížnější porozumět), když se snažíte posunout za hranice jednoduchého. Když se podíváte na níže uvedený příklad (s laskavým svolením Nicholase Zakase, protože jsem byl příliš líný napsat svůj vlastní jednoduchý kód), při vytváření Animal typu, vidíte nějakou zvláštnost, ale jakmile se přes to dostanete, není to těžké. Například, abychom vytvořili konstruktor, stačí vytvořit název funkce Animal . Je to zvláštní, ale překonáte to a není to velký problém. Jediný skutečný problém je v tom, že pokud všichni nedodržují konvence správně, je těžké poznat, kdy někdo právě píše funkci nebo jestli píše konstruktor. Konvence v pojmenování však pomáhají.

Každopádně, jdeme dál, vidíme, jak přidat metodu do Animal který bude dostupný všem jeho instancím. Pokud nejste dostatečně obeznámeni s JavaScriptem, prototype Klíčové slovo se vám může zdát trochu cizí, ale jakmile si na jeho používání zvyknete, není to žádná překážka. Jedinou chybou je čitelnost, jako obvykle, ale každý zkušený vývojář JavaScriptu to pozná.

1
2
3
4
5
6
7
function Animal(name) {
this.name = name;
}

Animal.prototype.sayName = function() {
console.log(this.name);
};

Nyní se dostaneme do problematické oblasti. V níže uvedeném kódu vytvoříme typ, který dědí z Animal s názvem Dog . Dvě věci mi přijdou jako „špatné“. Na řádku 2 se snažíme zavolat „super“ konstruktor, ale protože neexistuje žádný super klíčové slovo nebo cokoliv relativně podobného, ​​musíme použít nepříliš známou funkci JavaScriptu, konkrétně call nebo apply . Přestože existence těchto dvou funkcí začala být známější, stále jde o pokročilou funkci, kterou začátečníci pravděpodobně neznají. Rozhodně mi chvíli trvalo, než jsem se o nich dozvěděl. V každém případě to není elegantní.

Druhá nepříjemnost pochází ze snahy o stanovení dědictví, což se provádí pomocí tohoto kódu níže:Dog.prototype = new Animal(null); . Ani teď mi tento kód nedává moc smysl. Chápu, co se děje, ale nedává smysl vytvářet instanci typu, abyste z ní mohli dědit. Potenciální chyba se také může objevit, pokud je Animal konstruktor dělá cokoli kromě inicializace vnitřních vlastností, jako je manipulace s DOM. Vše, o co se snažíme, je dědit z Animal ale k tomu Animal se vytvoří a začne měnit stránku.

1
2
3
4
5
6
7
8
9
function Dog(name) {
Animal.call(this, name);
}

Dog.prototype = new Animal(null);

Dog.prototype.bark = function() {
console.log("Woof!");
};

Kvůli těmto zjevným problémům si mnoho knihoven vytvořilo vlastní způsob zacházení s tímto dědictvím, který jej zjednodušuje. Na prototypovém modelu dědičnosti není ve své podstatě nic špatného. Problémy pocházejí z práce, která je k tomu zapotřebí, a z nepochopení, které může pocházet z jeho syntaxe.

Další způsob

JavaScript je funkcionální programovací jazyk (i když nejen funkční), což je jeden z důvodů, proč to bylo až do této chvíle tak obtížné. Existuje další způsob, jak provádět dědění bez prototypů, který se více přibližuje funkční povaze JavaScriptu, který se při provádění veškeré práce zcela spoléhá na objektové literály a funkce. Je to velmi zajímavá alternativa, ale ještě více se vzdaluje tomu, na co jsou programátoři v Javě a C++ zvyklí. Pokud vás to zajímá, Toby Ho má skvělý článek vysvětlující JavaScript OO bez konstruktorů.

Cesta ES6

V ECMAScript 6 uvidíme zavedení „tříd“. Mnoho, mnoho lidí tvrdí, že jsou zbytečné, a technicky jsou. Třídy ES6 ani nejsou třídami; jsou syntaktickým cukrem, který usnadňuje náš vývoj. To je vše. Třídy nejsou zcela novou konstrukcí v JavaScriptu, jsou pouze novým způsobem, jak říci přesně totéž, kromě toho, že to dává větší smysl a je jednodušší používat. Podívejte se na následující kód, který přepisuje předchozí příklad pomocí tříd ES6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal {
constructor(name) {
this.name = name;
}

sayName() {
console.log(this.name);
}
}

class Dog extends Animal {
constructor(name) {
super(name);
}

bark() {
console.log("Woof!");
}
}

Kdybyste pocházeli z prostředí s klasickým OO jazykem, nedávalo by to pro vás dokonalý smysl? Je to jasné, stručné a jednoduché. Dostanete přesně to, co si myslíte, že dostáváte. Také, i když je syntaxe nová, ve skutečnosti se zde nic nového neděje. Pořád dělá to samé jako předtím, akorát s čistším kódem. Stále existuje řetězení prototypů, metody jsou stále přidávány do prototypů, stále s nimi můžete manipulovat stejným způsobem jako se starou syntaxí, ale už nemusíte.

Závěr

Potřebujeme mít třídy v JavaScriptu? Ne, ale rozhodně vyčistí způsob, jakým definujeme typy a provádíme dědičnost v JavaScriptu, což nikdy nemůže být špatné. Pomáhá také vývojářům přicházejícím z jiných jazyků o něco snadněji se dozvědět o JavaScriptu. Jediný problém se vším, co jsem viděl s ES6, je, že po nějakou dobu nebude existovat slušné množství kompatibilních prohlížečů. Ale to je místo, kde přicházejí věci jako TypeScript.