JavaScript je prý jazyk založený na prototypech. Takže "prototypy" musí být důležitý pojem, ne?
Dnes vám vysvětlím, co jsou prototypy, co potřebujete vědět a jak prototypy efektivně používat.
Co jsou prototypy?
Za prvé nenechte se svést slovem „Prototyp“ . „Prototyp“ v JavaScriptu není totéž jako „prototyp“ v angličtině. Neznamená to počáteční verzi produktu, která byla rychle sestavena.
Místo toho je prototyp v JavaScriptu prostě slovo, které absolutně nic neznamená. Můžeme nahradit prototyp pomeranči a může to znamenat totéž.
Vzpomeňte si například na Apple. Než se Apple Computers staly populárními, pravděpodobně si představíte Apple jako ovoce červené barvy. „Apple“ v Apple Computers zpočátku nemá význam – ale nyní ano.
V případě JavaScriptu prototyp odkazuje na systém. Tento systém vám umožňuje definovat vlastnosti objektů, ke kterým lze přistupovat prostřednictvím instancí objektu.
:::Poznámka
Prototyp úzce souvisí s objektově orientovaným programováním. Nedávalo by to smysl, pokud nerozumíte, o čem je objektově orientované programování.
Než přejdete dále, doporučuji vám seznámit se s touto úvodní sérií o objektově orientovaném programování.
:::
Například Array
je plán pro instance pole. Instanci pole vytvoříte pomocí []
nebo new Array()
.
const array = ['one', 'two', 'three']
console.log(array)
// Same result as above
const array = new Array('one', 'two', 'three')
Pokud console.log
toto pole, nevidíte žádné metody. Ale přesto můžete použít metody jako concat
, slice
, filter
a map
!
Proč?
Protože tyto metody jsou umístěny v prototypu pole. __proto__
můžete rozšířit objekt (Chrome Devtools) nebo <prototype>
objekt (Firefox Devtools) a uvidíte seznam metod.
:::Poznámka
Oba __proto__
v Chrome a <prototype>
ve Firefoxu ukazuje na objekt Prototype. Jen se v různých prohlížečích píší jinak.
:::
Když použijete map
, JavaScript hledá map
v objektu samotném. Pokud map
nenalezen, JavaScript se pokouší vyhledat prototyp. Pokud JavaScript najde prototyp, pokračuje v hledání map
v tomto prototypu.
Takže správná definice prototypu je:Objekt, ke kterému mají instance přístup když se snaží hledat nemovitost.
Prototypové řetězy
Když přistupujete ke službě, JavaScript dělá toto:
Krok 1 :JavaScript zkontroluje, zda je vlastnost dostupná uvnitř objektu. Pokud ano, JavaScript tuto vlastnost okamžitě použije.
Krok 2 :Pokud vlastnost NENÍ uvnitř objektu, JavaScript zkontroluje, zda je k dispozici prototyp. Pokud existuje prototyp, opakujte krok 1 (a zkontrolujte, zda je vlastnost uvnitř prototypu).
Krok 3 :Pokud nezbývají žádné další prototypy a JavaScript nemůže najít vlastnost, provede následující:
- Vrátí
undefined
(pokud jste se pokusili o přístup ke službě). - Vyvolá chybu (pokud jste se pokusili volat metodu).
Schématicky tento proces vypadá takto:
Příklad řetězce prototypu
Řekněme, že máme Human
třída. Máme také Developer
Podtřída, která dědí z Human
. Human
s mají sayHello
metoda a Developers
mít code
metoda.
Zde je kód pro Human
class Human {
constructor(firstName, lastName) {
this.firstName = firstName
this.lastname = lastName
}
sayHello () {
console.log(`Hi, I'm ${this.firstName}`)
}
}
:::Poznámka
Human
(a Developer
níže) lze psát pomocí funkcí konstruktoru. Pokud použijeme funkce konstruktoru, prototype
bude jasnější, ale vytváření podtříd bude obtížnější. Proto ukazuji příklad s Classes. (V tomto článku najdete 4 různé způsoby použití objektově orientovaného programování).
Zde je návod, jak byste napsali Human
pokud jste místo toho použili konstruktor.
function Human (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
Human.prototype.sayHello = function () {
console.log(`Hi, I'm ${this.firstName}`)
}
:::
Zde je kód pro Developer
.
class Developer extends Human {
code (thing) {
console.log(`${this.firstName} coded ${thing}`)
}
}
A Developer
instance může použít obě code
a sayHello
protože tyto metody jsou umístěny v řetězci prototypů instance.
const zell = new Developer('Zell', 'Liew')
zell.sayHello() // Hi, I'm Zell
zell.code('website') // Zell coded website
Pokud console.log
instance, můžete vidět metody v řetězci prototypů.
Prototypové delegování / Prototypová dědičnost
Prototypální delegování a Prototypální dědění znamenají totéž.
Jednoduše říkají, že používáme prototypový systém – kde vkládáme vlastnosti a metody do prototype
objekt.
Měli bychom použít Prototypal Delegation?
Protože JavaScript je jazyk založený na prototypech, měli bychom používat Prototypal Delegation. Správně?
Vlastně ne.
Řekl bych, že to závisí na tom, jak píšete objektově orientované programování. Prototypy má smysl používat, pokud používáte třídy, protože jsou pohodlnější.
class Blueprint {
method1 () {/* ... */}
method2 () {/* ... */}
method3 () {/* ... */}
}
Ale dává smysl NEPOUŽÍVAT prototypy, pokud používáte funkce Factory.
function Blueprint {
return {
method1 () {/* ... */}
method2 () {/* ... */}
method3 () {/* ... */}
}
}
Znovu si přečtěte tento článek o čtyřech různých způsobech psaní objektově orientovaného programování.
Výkonnostní důsledky
Na výkonu mezi těmito dvěma metodami příliš nezáleží – pokud vaše aplikace nevyžaduje miliony operací. V této části se podělím o několik experimentů, které tento bod dokážou.
Nastavení
Můžeme použít performance.now
pro přihlášení časového razítka před spuštěním jakékoli operace. Po spuštění operací použijeme performance.now
pro opětovné přihlášení časového razítka.
Poté získáme rozdíl v časových razítkách, abychom změřili, jak dlouho operace trvaly.
const start = performance.now()
// Do stuff
const end = performance.now()
const elapsed = end - start
console.log(elapsed)
Použil jsem perf
funkce, která mi pomůže s mými testy:
function perf (message, callback, loops = 1) {
const startTime = performance.now()
for (let index = 0; index <= loops; index++) {
callback()
}
const elapsed = performance.now() - startTime
console.log(message + ':', elapsed)
}
Poznámka:Můžete se dozvědět více o performance.now
v tomto článku.
Experiment č. 1:Používání prototypů vs. nepoužívání prototypů
Nejprve jsem testoval, jak dlouho trvá přístup k metodě prostřednictvím prototypu oproti jiné metodě, která se nachází v samotném objektu.
Zde je kód:
class Blueprint () {
constructor () {
this.inObject = function () { return 1 + 1 }
}
inPrototype () { return 1 + 1 }
}
const count = 1000000
const instance = new Blueprint()
perf('In Object', _ => { instance.inObject() }, count)
perf('In Prototype', _ => { instance.inPrototype() }, count)
Průměrné výsledky jsou v této tabulce shrnuty takto:
Test | 1 000 000 operací | 10 000 000 operací |
---|---|---|
V objektu | 3 ms | 15 ms |
V prototypu | 2 ms | 12 ms |
Poznámka:Výsledky jsou z Devtools Firefoxu. Přečtěte si toto, abyste pochopili, proč srovnávám pouze s Firefoxem.
Verdikt:Nezáleží na tom, zda používáte prototypy nebo ne. Pokud neprovedete> 1 milion operací, nezmění se to.
Experiment č. 2:Třídy vs tovární funkce
Tento test jsem musel spustit, protože doporučuji používat prototypy, když používáte třídy, a nepoužívat prototypy, když používáte funkce Factory.
Potřeboval jsem otestovat, zda je vytváření funkcí Factory výrazně pomalejší než vytváření tříd.
Tady je kód.
// Class blueprint
class HumanClass {
constructor (firstName, lastName) {
this.firstName = firstName
this.lastName = lastName
}
sayHello () {
console.lg(`Hi, I'm ${this.firstName}}`)
}
}
// Factory blueprint
function HumanFactory (firstName, lastName) {
return {
firstName,
lastName,
sayHello () {
console.log(`Hi, I'm ${this.firstName}}`)
}
}
}
// Tests
const count = 1000000
perf('Class', _ => { new HumanClass('Zell', 'Liew') }, count)
perf('Factory', _ => { HumanFactory('Zell', 'Liew') }, count)
Průměrné výsledky jsou shrnuty v tabulce takto:
Test | 1 000 000 operací | 10 000 000 operací |
---|---|---|
Třída | 5 ms | 18 ms |
Továrna | 6 ms | 18 ms |
Verdikt:Nezáleží na tom, zda používáte funkce Class nebo Factory. Nebude to mít žádný rozdíl, i když spustíte> 1 milion operací.
Závěr o testech výkonu
Můžete použít funkce Classes nebo Factory. Můžete se rozhodnout používat prototypy, nebo ne. Je to opravdu na vás.
O výkon si nemusíte dělat starosti.
Děkuji za přečtení. Tento článek byl původně zveřejněn na mém blogu. Přihlaste se k odběru mého newsletteru, pokud chcete další články, které vám pomohou stát se lepším vývojářem frontendu.