"prototype"
vlastnost je široce používána samotným jádrem JavaScriptu. Používají jej všechny vestavěné funkce konstruktoru.
Nejprve se podíváme na detaily a poté na to, jak to použít pro přidávání nových funkcí do vestavěných objektů.
Object.prototype
Řekněme, že vypíšeme prázdný objekt:
let obj = {};
alert( obj ); // "[object Object]" ?
Kde je kód, který generuje řetězec "[object Object]"
? To je vestavěný toString
metoda, ale kde to je? obj
je prázdný!
…Ale krátký zápis obj = {}
je stejný jako obj = new Object()
, kde Object
je vestavěná funkce konstruktoru objektů s vlastním prototype
odkazování na obrovský objekt pomocí toString
a další metody.
Zde je to, co se děje:
Když new Object()
se volá (nebo doslovný objekt {...}
je vytvořen), [[Prototype]]
z toho je nastaveno na Object.prototype
podle pravidla, o kterém jsme hovořili v předchozí kapitole:
Takže když obj.toString()
se nazývá metoda je převzata z Object.prototype
.
Můžeme to zkontrolovat takto:
let obj = {};
alert(obj.__proto__ === Object.prototype); // true
alert(obj.toString === obj.__proto__.toString); //true
alert(obj.toString === Object.prototype.toString); //true
Vezměte prosím na vědomí, že již není [[Prototype]]
v řetězci nad Object.prototype
:
alert(Object.prototype.__proto__); // null
Další vestavěné prototypy
Další vestavěné objekty, jako je Array
, Date
, Function
a další také uchovávají metody v prototypech.
Například, když vytvoříme pole [1, 2, 3]
, výchozí new Array()
konstruktor se používá interně. Takže Array.prototype
se stává jeho prototypem a poskytuje metody. To je velmi efektivní z hlediska paměti.
Podle specifikace mají všechny vestavěné prototypy Object.prototype
na vrcholu. Proto někteří lidé říkají, že „všechno se dědí z předmětů“.
Zde je celkový obrázek (pro 3 vestavěné moduly):
Pojďme zkontrolovat prototypy ručně:
let arr = [1, 2, 3];
// it inherits from Array.prototype?
alert( arr.__proto__ === Array.prototype ); // true
// then from Object.prototype?
alert( arr.__proto__.__proto__ === Object.prototype ); // true
// and null on the top.
alert( arr.__proto__.__proto__.__proto__ ); // null
Některé metody v prototypech se mohou překrývat, například Array.prototype
má svůj vlastní toString
který uvádí prvky oddělené čárkami:
let arr = [1, 2, 3]
alert(arr); // 1,2,3 <-- the result of Array.prototype.toString
Jak jsme viděli dříve, Object.prototype
má toString
také, ale Array.prototype
je v řetězci blíže, takže se použije varianta pole.
Nástroje v prohlížeči, jako je konzole pro vývojáře Chrome, také zobrazují dědičnost (console.dir
může být nutné použít pro vestavěné objekty):
Stejně fungují i další vestavěné objekty. Dokonce i funkce – jsou objekty vestavěného Function
konstruktor a jejich metody (call
/apply
a další) jsou převzaty z Function.prototype
. Funkce mají vlastní toString
taky.
function f() {}
alert(f.__proto__ == Function.prototype); // true
alert(f.__proto__.__proto__ == Object.prototype); // true, inherit from objects
Primitiva
Nejsložitější věc se stane s řetězci, čísly a booleany.
Jak si pamatujeme, nejsou to předměty. Ale pokud se pokusíme získat přístup k jejich vlastnostem, vytvoří se dočasné obalové objekty pomocí vestavěných konstruktorů String
, Number
a Boolean
. Poskytnou metody a zmizí.
Tyto objekty jsou pro nás vytvářeny neviditelně a většina motorů je optimalizuje, ale specifikace to popisuje přesně takto. Metody těchto objektů také sídlí v prototypech, dostupných jako String.prototype
, Number.prototype
a Boolean.prototype
.
null
a undefined
nemají žádné obaly objektů
Speciální hodnoty null
a undefined
stát od sebe. Nemají žádné obaly objektů, takže metody a vlastnosti pro ně nejsou dostupné. A neexistují ani odpovídající prototypy.
Změna nativních prototypů
Nativní prototypy lze upravovat. Pokud například přidáme metodu do String.prototype
, bude dostupný pro všechny řetězce:
String.prototype.show = function() {
alert(this);
};
"BOOM!".show(); // BOOM!
Během procesu vývoje můžeme mít nápady na nové vestavěné metody, které bychom chtěli mít, a můžeme být v pokušení přidat je do nativních prototypů. Ale to je obecně špatný nápad.
Důležité:
Prototypy jsou globální, takže je snadné dostat se do konfliktu. Pokud dvě knihovny přidají metodu String.prototype.show
, pak jeden z nich přepíše metodu druhého.
Obecně se tedy úprava nativního prototypu považuje za špatný nápad.
V moderním programování existuje pouze jeden případ, kdy je úprava nativních prototypů schválena. To je polyfilling.
Polyfilling je termín pro vytvoření náhrady za metodu, která existuje ve specifikaci JavaScriptu, ale zatím není podporována konkrétním JavaScriptovým enginem.
Poté jej můžeme implementovat ručně a naplnit jím vestavěný prototyp.
Například:
if (!String.prototype.repeat) { // if there's no such method
// add it to the prototype
String.prototype.repeat = function(n) {
// repeat the string n times
// actually, the code should be a little bit more complex than that
// (the full algorithm is in the specification)
// but even an imperfect polyfill is often considered good enough
return new Array(n + 1).join(this);
};
}
alert( "La".repeat(3) ); // LaLaLa
Výpůjčky od prototypů
V kapitole Dekoratéři a přeposílání, volání/žádost jsme hovořili o výpůjčce metod.
To je, když vezmeme metodu z jednoho objektu a zkopírujeme ji do jiného.
Některé metody nativních prototypů jsou často vypůjčovány.
Pokud například vytváříme objekt podobný poli, můžeme chtít zkopírovat nějaký Array
metody.
Např.
let obj = {
0: "Hello",
1: "world!",
length: 2,
};
obj.join = Array.prototype.join;
alert( obj.join(',') ); // Hello,world!
Funguje to díky internímu algoritmu vestavěného join
metoda se stará pouze o správné indexy a length
vlastnictví. Nekontroluje, zda je objekt skutečně pole. Mnoho vestavěných metod je podobných.
Další možností je dědit nastavením obj.__proto__
na Array.prototype
, takže všechny Array
metody jsou automaticky dostupné v obj
.
Ale to je nemožné, pokud obj
již dědí z jiného objektu. Pamatujte, že můžeme dědit vždy pouze z jednoho objektu.
Metody výpůjček jsou flexibilní, umožňují v případě potřeby kombinovat funkce z různých objektů.
Shrnutí
- Všechny vestavěné objekty se řídí stejným vzorem:
- Metody jsou uloženy v prototypu (
Array.prototype
,Object.prototype
,Date.prototype
atd.) - Samotný objekt ukládá pouze data (položky pole, vlastnosti objektu, datum)
- Metody jsou uloženy v prototypu (
- Primitiva také ukládají metody do prototypů objektů wrapper:
Number.prototype
,String.prototype
aBoolean.prototype
. Pouzeundefined
anull
nemají obalové objekty - Vestavěné prototypy lze upravit nebo naplnit novými metodami. Ale nedoporučuje se je měnit. Jediným přípustným případem je pravděpodobně to, že přidáme nový standard, ale ten zatím není podporován jádrem JavaScript