V praxi často potřebujeme vytvořit mnoho objektů stejného druhu, jako jsou uživatelé, zboží nebo cokoli jiného.
Jak již víme z kapitoly Konstruktor, operátor "new", new function
s tím může pomoci.
Ale v moderním JavaScriptu existuje pokročilejší konstrukce „class“, která zavádí skvělé nové funkce, které jsou užitečné pro objektově orientované programování.
Syntaxe „třídy“
Základní syntaxe je:
class MyClass {
// class methods
constructor() { ... }
method1() { ... }
method2() { ... }
method3() { ... }
...
}
Poté použijte new MyClass()
k vytvoření nového objektu pomocí všech uvedených metod.
constructor()
metoda je volána automaticky pomocí new
, takže tam můžeme objekt inicializovat.
Například:
class User {
constructor(name) {
this.name = name;
}
sayHi() {
alert(this.name);
}
}
// Usage:
let user = new User("John");
user.sayHi();
Když new User("John")
se jmenuje:
- Je vytvořen nový objekt.
constructor
spustí s daným argumentem a přiřadí jejthis.name
.
…Pak můžeme volat objektové metody, jako je user.sayHi()
.
Běžným úskalím pro začínající vývojáře je vložení čárky mezi metody třídy, což by vedlo k chybě syntaxe.
Zde uvedený zápis nesmí být zaměňován s objektovými literály. V rámci třídy nejsou vyžadovány žádné čárky.
Co je to třída?
Takže, co přesně je class
? To není úplně nová entita na jazykové úrovni, jak by si někdo mohl myslet.
Odhalme jakékoli kouzlo a podívejme se, co třída skutečně je. To vám pomůže pochopit mnoho složitých aspektů.
V JavaScriptu je třída druh funkce.
Zde se podívejte:
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// proof: User is a function
alert(typeof User); // function
Co class User {...}
konstrukt skutečně je:
- Vytvoří funkci s názvem
User
, která se stane výsledkem deklarace třídy. Kód funkce je převzat zconstructor
metoda (předpokládá se prázdná, pokud takovou metodu nenapíšeme). - Ukládá metody třídy, jako je
sayHi
, vUser.prototype
.
Po new User
objekt je vytvořen, když zavoláme jeho metodu, je převzat z prototypu, jak je popsáno v kapitole F.prototyp. Objekt má tedy přístup k metodám třídy.
Můžeme ilustrovat výsledek class User
prohlášení jako:
Zde je kód, jak si to prohlédnout:
class User {
constructor(name) { this.name = name; }
sayHi() { alert(this.name); }
}
// class is a function
alert(typeof User); // function
// ...or, more precisely, the constructor method
alert(User === User.prototype.constructor); // true
// The methods are in User.prototype, e.g:
alert(User.prototype.sayHi); // the code of the sayHi method
// there are exactly two methods in the prototype
alert(Object.getOwnPropertyNames(User.prototype)); // constructor, sayHi
Nejen syntaktický cukr
Někdy lidé říkají, že class
je „syntaktický cukr“ (syntaxe, která je navržena tak, aby usnadnila čtení věcí, ale nepřináší nic nového), protože bychom mohli deklarovat totéž bez použití class
vůbec klíčové slovo:
// rewriting class User in pure functions
// 1. Create constructor function
function User(name) {
this.name = name;
}
// a function prototype has "constructor" property by default,
// so we don't need to create it
// 2. Add the method to prototype
User.prototype.sayHi = function() {
alert(this.name);
};
// Usage:
let user = new User("John");
user.sayHi();
Výsledek této definice je přibližně stejný. Takže skutečně existují důvody, proč class
lze považovat za syntaktický cukr k definování konstruktoru spolu s jeho prototypovými metodami.
Přesto jsou zde důležité rozdíly.
-
Za prvé, funkce vytvořená
class
je označeno speciální interní vlastností[[IsClassConstructor]]: true
. Není to tedy úplně stejné, jako když jej vytvoříte ručně.Jazyk kontroluje danou nemovitost na různých místech. Například na rozdíl od běžné funkce musí být volána s
new
:class User { constructor() {} } alert(typeof User); // function User(); // Error: Class constructor User cannot be invoked without 'new'
Řetězcová reprezentace konstruktoru třídy ve většině JavaScriptových motorů také začíná „třídou…“
class User { constructor() {} } alert(User); // class User { ... }
Existují další rozdíly, brzy je uvidíme.
-
Metody třídy nejsou vyčíslitelné. Definice třídy nastavuje
enumerable
příznak nafalse
pro všechny metody v"prototype"
.To je dobře, protože pokud
for..in
nad objektem, obvykle nechceme jeho metody třídy. -
Třídy vždy
use strict
.Veškerý kód uvnitř konstrukce třídy je automaticky v přísném režimu.
Kromě toho class
syntaxe přináší mnoho dalších funkcí, které prozkoumáme později.
Výraz třídy
Stejně jako funkce mohou být třídy definovány uvnitř jiného výrazu, předány, vráceny, přiřazeny atd.
Zde je příklad výrazu třídy:
let User = class {
sayHi() {
alert("Hello");
}
};
Podobně jako výrazy pojmenovaných funkcí mohou mít výrazy třídy název.
Pokud má výraz třídy název, je viditelný pouze uvnitř třídy:
// "Named Class Expression"
// (no such term in the spec, but that's similar to Named Function Expression)
let User = class MyClass {
sayHi() {
alert(MyClass); // MyClass name is visible only inside the class
}
};
new User().sayHi(); // works, shows MyClass definition
alert(MyClass); // error, MyClass name isn't visible outside of the class
Můžeme dokonce vytvářet třídy dynamicky „na vyžádání“ takto:
function makeClass(phrase) {
// declare a class and return it
return class {
sayHi() {
alert(phrase);
}
};
}
// Create a new class
let User = makeClass("Hello");
new User().sayHi(); // Hello
Getters/setters
Stejně jako doslovné objekty mohou třídy zahrnovat getry/settery, vypočítané vlastnosti atd.
Zde je příklad pro user.name
implementováno pomocí get/set
:
class User {
constructor(name) {
// invokes the setter
this.name = name;
}
get name() {
return this._name;
}
set name(value) {
if (value.length < 4) {
alert("Name is too short.");
return;
}
this._name = value;
}
}
let user = new User("John");
alert(user.name); // John
user = new User(""); // Name is too short.
Technicky taková deklarace třídy funguje tak, že vytváří getry a settery v User.prototype
.
Vypočítané názvy […]
Zde je příklad s vypočítaným názvem metody pomocí hranatých závorek [...]
:
class User {
['say' + 'Hi']() {
alert("Hello");
}
}
new User().sayHi();
Tyto vlastnosti jsou snadno zapamatovatelné, protože se podobají doslovným objektům.
Pole třídy
Staré prohlížeče mohou vyžadovat polyfillPole třídy jsou nedávným přírůstkem do jazyka.
Dříve měly naše třídy pouze metody.
„Pole třídy“ je syntaxe, která umožňuje přidat libovolné vlastnosti.
Přidejme například name
vlastnost na class User
:
class User {
name = "John";
sayHi() {
alert(`Hello, ${this.name}!`);
}
}
new User().sayHi(); // Hello, John!
Takže do deklarace napíšeme "
Důležitý rozdíl v polích tříd je v tom, že se nastavují na jednotlivé objekty, nikoli na User.prototype
:
class User {
name = "John";
}
let user = new User();
alert(user.name); // John
alert(User.prototype.name); // undefined
Hodnoty můžeme také přiřadit pomocí složitějších výrazů a volání funkcí:
class User {
name = prompt("Name, please?", "John");
}
let user = new User();
alert(user.name); // John
Vytváření vázaných metod s poli tříd
Jak je ukázáno v kapitole Funkce vazby funkcí v JavaScriptu mají dynamický this
. Záleží na kontextu hovoru.
Pokud je tedy objektová metoda předávána a volána v jiném kontextu, this
již nebude odkazem na jeho objekt.
Tento kód například zobrazí undefined
:
class Button {
constructor(value) {
this.value = value;
}
click() {
alert(this.value);
}
}
let button = new Button("hello");
setTimeout(button.click, 1000); // undefined
Problém se nazývá „ztráta this
".
Existují dva způsoby, jak to opravit, jak je popsáno v kapitole Vazba funkcí:
- Předejte funkci wrapper, například
setTimeout(() => button.click(), 1000)
. - Připojte metodu k objektu, např. v konstruktoru.
Pole tříd poskytují další, docela elegantní syntaxi:
class Button {
constructor(value) {
this.value = value;
}
click = () => {
alert(this.value);
}
}
let button = new Button("hello");
setTimeout(button.click, 1000); // hello
Pole třídy click = () => {...}
je vytvořen na základě jednotlivých objektů, pro každý Button
existuje samostatná funkce objekt s this
uvnitř ní odkazující na tento objekt. Můžeme předat button.click
kdekoli a hodnotu this
bude vždy správné.
To je užitečné zejména v prostředí prohlížeče pro posluchače událostí.
Shrnutí
Základní syntaxe třídy vypadá takto:
class MyClass {
prop = value; // property
constructor(...) { // constructor
// ...
}
method(...) {} // method
get something(...) {} // getter method
set something(...) {} // setter method
[Symbol.iterator]() {} // method with computed name (symbol here)
// ...
}
MyClass
je technicky funkce (ta, kterou poskytujeme jako constructor
), zatímco metody, gettry a settery se zapisují do MyClass.prototype
.
V dalších kapitolách se dozvíme více o třídách, včetně dědičnosti a dalších funkcí.