Základní syntaxe třídy

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:

  1. Je vytvořen nový objekt.
  2. constructor spustí s daným argumentem a přiřadí jej this.name .

…Pak můžeme volat objektové metody, jako je user.sayHi() .

Žádná čárka mezi metodami třídy

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:

  1. Vytvoří funkci s názvem User , která se stane výsledkem deklarace třídy. Kód funkce je převzat z constructor metoda (předpokládá se prázdná, pokud takovou metodu nenapíšeme).
  2. Ukládá metody třídy, jako je sayHi , v User.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.

  1. 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.

  2. Metody třídy nejsou vyčíslitelné. Definice třídy nastavuje enumerable příznak na false pro všechny metody v "prototype" .

    To je dobře, protože pokud for..in nad objektem, obvykle nechceme jeho metody třídy.

  3. 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 polyfill

Pole 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 " =" a je to.

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í:

  1. Předejte funkci wrapper, například setTimeout(() => button.click(), 1000) .
  2. 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í.


No