Nativní prototypy

"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.prototypetoString 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 .

Hodnoty 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)
  • Primitiva také ukládají metody do prototypů objektů wrapper:Number.prototype , String.prototype a Boolean.prototype . Pouze undefined a null 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