Dědičnost v js nefunguje. JavaScript – vzory dědičnosti. Nová matoucí konstrukce

Velmi často návrháři kreslí rozvržení s nestandardními formulářovými prvky, tzn. Design zaškrtávacích políček, přepínačů, vstupů a selektorů se liší od toho, co prohlížeč vykresluje ve výchozím nastavení. Návrhář rozvržení tak stojí před úkolem přizpůsobit návrh, včetně vybraných prvků.

V tomto článku se podíváme na to, jak to změnit vzhled vybrat My se však budeme držet následující ideologie. Má smysl přizpůsobit pouze výchozí sbalený stav selektoru a design rozevíracího seznamu by měl zůstat standardní. Níže popsané řešení proto nebude fungovat, pokud potřebujete složitý rozevírací seznam, například s ikonami nebo zaškrtávacími políčky pro každou možnost.

Na druhou stranu tento přístup umožňuje:

1. Nepište javascriptový kód, který by emuloval chování selektorů. Veškerá logika spočívá na prohlížeči. Dnes existuje spousta pluginů, ale často nenarazíte na takový, který by zvládl všechny okrajové případy, se kterými se uživatel při interakci s tímto ovládacím prvkem setká. Příkladem takového extrémního případu by bylo automatický výběr pozice rozevíracího seznamu (otevření dolů nebo nahoru).

2. Vzhledem k tomu, že za chování a design rozbalovacího seznamu je zodpovědný prohlížeč, není třeba psát žádné samostatné řešení Pro mobilní zařízení, což by umožnilo pohodlnou práci s voliči na dotykových zařízeních s malými obrazovkami. Vestavěné nativní selektory samy akceptují správný typ, protože na mobilní platformy vše je již promyšleno. Když například uživatelé iOS kliknou na volič, neuvidí váš vlastní seznam, ale standardní známý. Uživatelé nebudou muset přizpůsobovat své chování vašemu konkrétnímu webu; jejich chování bude stejné jako na všech ostatních stránkách. A je to pro něj výhodné, a proto jste web vytvořili.

Jak již bylo řečeno, CSS3 vlastnosti stačí k návrhu selectu.

Hlavní vlastností, která vám umožní změnit vzhled selektoru, je vzhled . Přinutí prvek, aby se stal tím, čím chcete, aby byl, například můžete donutit prvek div, aby se stal tlačítkem. Pro naše účely je však zajímavá hodnota none , což znamená, že prvek není třeba vůbec stylovat. Tímto způsobem můžeme odstranit atribut select-specific - šipka dolů vpravo.

Vyberte ( -webkit-vzhled: žádný; -moz-vzhled: žádný; -ms-vzhled: žádný; vzhled: žádný !důležité; )

Jak vidíte, odstranění šipky nebylo obtížné, nyní zbývá pouze přidat několik triviálních vlastností CSS, jako je rámeček, písmo a tak dále. Zde lze samozřejmě přidat zaoblené rohy, stíny atd., vše tak, jak designér nakreslil do layoutu. Hlavní rozevírací seznam zůstává nativní.

Vyberte ( pozadí: průhledné; zobrazení: blok; šířka: 100 %; ohraničení: 1px plný #a7a7a7; barva: #32353a; font-family: 16/Arial, sans-serif; velikost písma: 16px; výška řádku: 1,4 ; tloušťka písma: normální; 7px 10px; -webkit-vzhled: žádný !

Teď už zbývá jen přidat jako pozadí šipku nebo nějakou jinou ikonu. Použijeme pozadí, protože pseudoprvky jako předtím a potom nebudou pro select fungovat. Pro lepší zobrazení stránek na různá zařízení s různou hustotou pixelů, at různá měřítka atd. Je běžné používat ikony SVG. Proto pomocí online kodéru URL pro SVG převedeme ikonu na datové URI. Je důležité si uvědomit, že značka SVG musí mít atribut xmlns="http://www.w3.org/2000/svg".

Dostanete následující výsledek.

%3Csvg xmlns="http://www.w3.org/2000/svg" width="8" height="8" viewBox="0 0 8 8"%3E%3Cpath fill="%23000" fill-rule ="evenodd" class="cls-1" d="M8,0L4.141,6.993,0.012,0.156Z"/%3E%3C/svg%3E%0A

Vzhledem k tomu, že použitá šipka je nestandardní, můžete ji přesunout doleva nebo místo šipky použít jakýkoli jiný obrázek, případně se obejdete zcela bez ikon.

S příchodem CSS3 bylo možné vytvořit krásný styl pro jakýkoli prvek na webu. Protože CSS3 poskytuje širokou škálu možností, které urychlují proces vývoje designu webových stránek. Dnes si nastylujeme element select v CSS. Pro ty, kteří nevědí, je tento prvek zodpovědný za rozevírací seznam na webu. Mnoho lidí používá standardní zobrazení, ale můžete ho změnit tak, aby vyhovovalo designu vašeho webu.

Není nic lepšího, než vidět, jak seznam vypadá na vlastní oči:

Stáhnout

Takto vypadá výběr v CSS:

Podobné články na toto téma:

Nyní popíšu proces instalace krok za krokem pomocí tohoto rozevíracího seznamu nebo jednoduše Vybrat.

1 krok. Připojíme potřebné soubory

Je to jednoduché. Po stažení archivu se zdroji odtud budeme potřebovat 2 soubory (style.css a select.js - pokud připojujete první možnost seznamu nebo select_demo2.js - pokud připojujete druhou možnost). Mezi tagy propojíme tyto dva soubory:

1 2
Krok 2 Struktura HTML prvku Select

Ve struktuře není nic extra složitého (a proč by tam mělo být, je to jen HTML :)). Jednoduchý formulář obsahující rozevírací seznam Vybrat s jeho položkami:

1 2 3 4 5 6 7 8 9 10 11 12 Země Spojeného království: Vyberte prosím zemi: Anglie Severní Irsko Skotsko Wales
Krok 3 Přidávání stylů pro Vyberte CSS

Není jich mnoho. Níže uvádím styly pro první možnost seznamu. Rád bych vás upozornil na cesty k obrázkům. Jsou pouze dva: první slouží k otevření seznamu a druhý k uzavření. Vypadají jako dvě šipky „nahoru“ a „dolů“. Lze je stáhnout ze zdrojového kódu umístěného na začátku článku:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 .dropcontainer ( position : relativní ; font-size : 16px ; color : #777 ; ) .trigger ( color : #777 ; padding : 10px ; font-size : 16px ; width : 50 % ; background : #fff url () 98 % center no-repeat : blok : 1px solid #ccc ; all 0.5s easy; transition: all 0.5s easy) .trigger: hover ( color: #777; background: #f5f5f5 url (../images/select-arrow-open.png ) 98% střed bez opakování; ) . activetrigger ( color : #777 ; padding : 10px ; font-size : 16px ; width : 50 % ; background : #f5f5f5 url () 98% center no-repeat ; display : block ; border : 1px solid #ccc ; -webkit- box-sizing : border-box ) .activetrigger :hover ( background : #f5f5f5 url (../images/select-arrow-close.png ) 98% střed bez opakování ;

barva : #777 ;

) .activetrigger :active ( pozadí : #f5f5f5 url (../images/select-arrow-close.png ) 98 % střed bez opakování ; barva : #777 ; ) .dropcontainer ul ( velikost písma : 16px ; ohraničení : 1px solid #ccc : #fff : šířka : 50% ; dropcontainer ul li ( padding : 5px ; -webkit-transition : všech 0,5 s lehkost; -moz-transition : všech 0,5 s lehkost; -o-transition : všech 0,5 s lehkost; přechod : všech 0,5 s lehkost ) .dropcontainer ul li : hover ( background : #f5f5f5 ; obrys : žádný ; ) .dropcontainer ul li :first-child ( display : none ; ) .dropcontainer ul li :last-child ( border-bottom : none ; ) .dropdownhidden ( display) : none ; .dropdownvisible (výška: auto;)

Musíte nahradit poslední vlastnost dropdownvisible:

1 2 3 .dropdownvisible (výška: auto;)
1 2 3 4 .dropdownvisible (výška : 200px ; overflow-y : scroll ; )

A pokud nezapomenete nahradit skripty (viz výše, co změnit na co), získáte následující:

Ve kterých prohlížečích tento Select CSS (rozbalovací seznam) funguje dobře?
  • ✓ Firefox 24.0, Firefox 25.0, Firefox 26.0
  • ✓ Chrome 29.0, Chrome 30.0, Chrome 31.0
  • ✓ Opera 12.14, Opera 12.15, Opera 12.16
  • ✓ IE 7.0, IE 8.0, IE 9.0, IE 10.0, IE 11.0
  • ✓ Safari 5.1, Safari 6.0, Safari 6.1, Safari 7.0
  • ✓ Apple iOS – iPhone 4S, iPhone 5, iPad 2 (5.0), iPad 3 (6.0), iPad Mini
  • ✓ Android – Sony Experia X10, HTC One X, Kindle Fire 2, Google Nexus
Dodatek k lekci - kreativní hover efekt + VIDEO

Kromě lekce vám chci říci, jak udělat další efekt na webu velmi neobvyklý: efekt vznesení. Podívejte se na toto krátké video a přesvědčte se sami.

Závěr

Další prvek webu, Select, lze změnit tak, aby vyhovoval vašemu návrhu, pomocí CSS a Javascriptu. V procesu instalace není nic složitého, takže budete úspěšní. Jako doplněk k článku také obdržíte kreativní metodu a instalační video.

Pozor! Další vývoj a podpora pluginu byla zastavena z důvodu, že je nyní součástí .

Jednou z nejnepříjemnějších (a dokonce bych řekl strašných) věcí ve vývoji webu je rozložení html formulářů. Bohužel neexistuje jednotný standard pro zobrazování prvků formuláře, bez ohledu na prohlížeč a operační systém, stejně jako neexistuje způsob, jak některé z těchto prvků přizpůsobit pomocí kaskádových stylů.

Následující prvky formuláře HTML nelze plně stylizovat:

  • rozevírací seznam;
  • zaškrtávací políčko;
  • přepínač .
  • pole pro odeslání souboru.

Jak je již z názvu příspěvku zřejmé, zde se budeme bavit pouze o selectech.

Existuje mnoho hotových řešení v podobě jQuery pluginů pro stylování rozbalovacích seznamů. Ale já (vzhledem k tomu, že mi žádný z pluginů z toho či onoho důvodu nevyhovoval) jsem se rozhodl znovu vynalézt své vlastní kolo a napsal jsem vlastní plugin, který sdílím v tomto článku.

Na to bych chtěl hned upozornit tento plugin nehodí se pro všechny možné případy použití selectů (přečtěte si nevýhody).

Ukázka pluginu

Můžete vidět příklad stylingu selektoru pomocí mého pluginu. Navrhl jsem je bez použití obrázků.

Výhody
  • Když je JavaScript zakázán, zobrazí se standardní selektory.
  • Skript má malou velikost, přibližně 4 kilobajty.
  • Funguje v IE6+ a všech moderních desktopových prohlížečích.
  • Zobrazeno inline.
  • Snadno stylizovat pomocí CSS.
  • Umožňuje nastavit maximální výška pro rozevírací seznam (vlastnost CSS max-height).
  • Automaticky upraví šířku, pokud není zadána.
  • Podporuje rolování kolečkem myši.
  • Má „chytré polohování“, tzn. nepřesahuje při otevírání seznamu viditelnou část stránky.
  • Dokáže „chytit“ stisknutí klávesy Tab a přepínat pomocí šipek na klávesnici.
  • Podporuje atribut "disabled".
  • Funguje také s dynamicky přidanými/změněnými výběry.
Nedostatky
  • Nepodporuje atribut multiple, tzn. neumožňuje výběr více položek (multi-select).
  • Nepodporuje seskupování položek seznamu (tag).
  • Nepodporuje přepínání pomocí šipek na klávesnici, když je seznam otevřen kliknutím myši.
Stáhnout

Plugin není dostupný, protože už to není relevantní.

Plugin jQuery „SelectBox Styler“

Verze: 1.0.1 | Staženo: 11103 | Velikost: 7 KB | Poslední aktualizace: 10/07/2012

Aktualizace 22. 9. 2012 Převedl skript na plugin (včetně minimalizované verze) a také přidal podporu pro dynamické přidávání/změnu výběrů. 10/07/2012 Opraveno chování skriptu při použití metody onchange tagu. Připojení pluginu

Pokud web ještě nemá povolený jQuery, přidejte před značku následující řádek:

Ihned po jQuery zahrňte soubor se skriptem:

(function($) ( $(function() ( $("select").selectbox(); )) ))(jQuery)

Umístěte tento kód před značku za výše uvedené soubory.

Když měníte výběry dynamicky, musíte spustit spouštěč obnovení, například:

(funkce($) ( $(funkce() ( $("tlačítko").click(funkce() ( $("vybrat").find("option:nth-child(5)").attr("vybrané ", true); $("select").trigger("refresh"); )) )) ))(jQuery)

HTML kód po spuštění pluginu

Jeho struktura vypadá takto:

-- Vybrat --

  • -- Vybrat --
  • bod 1
  • bod 2
  • bod 3
-- Vybrat -- Položka 1 Položka 2 Položka 3

Třídy CSS používané ke stylování selektoru

Chcete-li selektory stylů pomocí CSS, použijte následující třídy:

.výběrové polenadřazený kontejner pro celý výběr
.výběrové pole .vybratvyberte ve sbaleném stavu
.selectbox.focused .selectzaměřte se na výběr při stisknutí klávesy Tab
.selectbox .select .textvnořený tag pro sbalený výběr v případě vložení obrázku na pozadí pomocí techniky „sliding door“.
.výběrové pole .spouštěčpravá strana sbaleného výběru (podmíněné přepnutí)
.selectbox .trigger .arrowdílčí značka pro přepínač (šipka)
.selectbox .rozbalovací nabídkaobal pro rozevírací seznam
.výběrové pole .rozbalovací ulrozevírací seznam
.selectbox livybrat položku (možnost)
.selectbox li.vybranývybraná vybrat položku
.výběrové pole li.deaktivovánozakázáno (není k dispozici pro výběr) vyberte položku
Závěr

Vytvoření takového skriptu je poměrně náročný úkol, protože musíte vzít v úvahu mnoho různých bodů. Opravdu doufám, že se neobjeví žádné závažné chyby. Ale kdyby něco, dejte mi prosím vědět do komentářů.

Začněme abstraktním příkladem:

Var a = (test: 11) b = a; b.test = 12; console.log(a.test); // Vytiskne 12!

K tomu dochází, protože objekty v JS jsou přiřazeny a předávány postupujte podle odkazu ne podle hodnoty.

Vlastnost .prototype je objekt. Když spustíte kód:

Bar.prototype = Foo.prototype;

přiřadíte vlastnosti Bar.prototype odkaz k objektu Foo.prototype. V důsledku toho má jakákoli změna vlastnosti Bar.prototype za následek změnu na Foo.prototype , jak je uvedeno ve výše uvedené citaci:

To znamená, že když začnete přiřazovat, například Bar.prototype.myLabel = ..., neupravujete samostatný objekt, ale samotný sdílený objekt Foo.prototype, což by ovlivnilo všechny objekty spojené s Foo.prototype.

Malá lyrická odbočka.

Bar.prototype = new Foo();

a všichni ti, kteří vám to radí – klidně pošlete mi, abych se naučil základy JS. Celý bod je v tom, že voláním new Foo() voláte konstruktor objektu. Navíc samotný konstruktor může na jedné straně uvalit omezení na předávané argumenty a na druhé straně může mít vedlejší účinky. Podívejme se na každý z těchto případů zvlášť.

Řekněme, že máte konstruktor, jako je tento, který ukládá omezení na jeho argumenty:

Foo = function(a) ( if (typeof a === "undefined") ( throw new Error("Musíte nastavit první argument."); ) this.a = a; )

V tomto případě již nemůžete pouze spustit:

Bar.prototype = new Foo();

protože potřebujete dovnitř výslovně předat konstruktoru argument, který je v době popisu hierarchie dědičnosti zcela nesmyslný. Nejzajímavější je, že hodnota parametru a bude stále přepsána při volání konstruktoru Foo v podřízeném konstruktoru Bar. Proto je nová konstrukce Foo() také nesmyslná.

Nyní předpokládejme, že nadřazený konstruktor má vedlejší účinky:

Foo = function(a) ( console.log("Tady jsem!"); )

Při použití:

Bar.prototype = new Foo();

a dále:

Var Bar = function() ( Foo.call(this); )

Řádek "Tady jsem!" bude vytištěn dvakrát. Souhlasíte, že to není vždy žádoucí chování systému.

No a ještě jedna zajímavost: i když in Teď nadřazený konstruktor nemá žádné vedlejší účinky nebo omezení argumentů, neznamená to, že to tak zůstane navždy. Je lepší udělat vše hned, než nervózně ladit kód a hledat chybu, když se vše rozbije.

Pro informaci je zde správná implementace dědičnosti v JS:

// Základní konstruktor var Foo = function() ( // ... ); Foo.prototype.doSomething = function() ( // ... ); // Podřízený konstruktor var Bar = function() ( // Volání základního konstruktoru pro aktuální objekt. Foo.call(this); // ... ); // Nastavte správnou hodnotu v řetězci prototypu. Bar.prototype = Object.create(Foo.prototype, ( // Nastavte správnou funkci konstruktoru pro všechny // vytvořené objekty. konstruktor: ( hodnota: Bar, vyčíslitelný: false, zapisovatelný: true, konfigurovatelný: true ) )); // Rozšíření prototypu podřízené "třídy". Tento krok musí přijít // PŘÍSNĚ PO nastavení hodnoty Bar.prototype. Bar.prototype.doAnotherAction = function() ( // ... );

V případě, že nemůžete použít Object.create (staré prohlížeče), můžete buď použít jeden ze stávajících polyfillů, nebo vše udělat ručně (přes anonymní konstruktor):

Var inherits = function(ctor, superCtor) ( // Dočasný konstruktor, který nic nedělá a je potřeba // pouze k přerušení přímého spojení mezi prototypy ctor // a superCtor. Pomocí něj můžete změnit prototyp // podřízený konstruktor bez obav z porušení nadřazeného . var Tmp = function() (); na předané hodnoty new Tmp(); // Nastavte správnou funkci konstruktoru pro všechny // vytvořené objekty ctor.prototype.constructor = ctor);

S přihlédnutím ke všemu výše uvedenému univerzální funkce dědictví může vypadat takto:

Var dědí = (function() ( if (typeof Object.create === "funkce") ( // Pokud existuje Object.create, použijte jednodušší možnost. return function(ctor, superCtor) ( ctor.prototype = Object.create ( superCtor.prototype, ( konstruktor: ( hodnota: ctor, enumerable: false, zapisovatelný: true, configurable: true ) ) ) // Použití dočasného konstruktoru pro starší prohlížeče return function(ctor, superCtor) ( var Tmp = function()); ; Tmp.prototype = superCtor.prototype = new Tmp();

Ve výše uvedených implementacích se po přiřazení prototypu nastaví vlastnost Function.prototype.constructor. Ačkoli se tato vlastnost v praxi používá jen zřídka (osobně jsem ji nikdy neviděl v produkčním kódu), měla by ji odhalit plná implementace dědičnosti.

JavaScript je pro vývojáře se zkušenostmi s jazyky založenými na třídách (jako Java nebo C++) trochu matoucí, protože je dynamický a neposkytuje implementaci třídy jako takovou (klíčové slovo class je zavedeno v ES2015, ale je to syntaktický cukr, JavaScript zůstává založen na prototypu).

Pokud jde o dědičnost, JavaScript má pouze jednu konstrukci: objekty. Každý objekt má soukromou vlastnost, která obsahuje odkaz na jiný objekt nazývaný jeho prototyp. Tento prototypový objekt má svůj vlastní prototyp a tak dále, dokud není dosaženo objektu s nulou jako prototypem. Podle definice nemá null žádný prototyp a funguje jako poslední článek v tomto prototypovém řetězci.

Dědění "metod"

JavaScript ano nemít„metody“ ve formě, ve které je třídní jazyky definují. V JavaScriptu lze k objektu přidat jakoukoli funkci ve formě vlastnosti. Zděděná funkce funguje stejně jako jakákoli jiná vlastnost, včetně stínování vlastnosti, jak je ukázáno výše (v tomto případě forma přeřazení metody).

Když je provedena zděděná funkce, hodnota tohoto ukazuje na dědící objekt, nikoli na prototypový objekt, kde je funkce vlastní vlastností.

Var o = ( a: 2, m: function() ( return this.a + 1; ) ); console.log(o.m()); // 3 // Při volání o.m v tomto případě "toto" odkazuje na o var p = Object.create(o); // p je objekt, který dědí z o p.a = 4; // vytvoří vlastnost "a" na p console.log(p.m()); // 5 // když se řekne p.m, "toto" odkazuje na p. // Takže když p zdědí funkci m z o, // "this.a" znamená p.a, vlastnost "a" z p

Použití prototypů v JavaScriptu

Podívejme se na to, co se děje v zákulisí, trochu podrobněji.

V JavaScriptu, jak je uvedeno výše, mohou mít funkce vlastnosti. Všechny funkce mají speciální vlastnost s názvem prototype . Vezměte prosím na vědomí, že níže uvedený kód je samostatně stojící (je bezpečné předpokládat, že na webové stránce není žádný jiný JavaScript než níže uvedený kód). Pro nejlepší zážitek z učení se důrazně doporučuje otevřít konzoli (což lze v Chrome a Firefoxu provést stisknutím Ctrl+Shift+I), přejít na kartu „konzole“, zkopírovat a vložit do pod kódem JavaScript a spusťte jej stisknutím klávesy Enter/Return.

Funkce doSomething()() console.log(doSomething.prototype); // Nezáleží na tom, jak funkci deklarujete, // funkce v JavaScriptu bude mít vždy výchozí vlastnost // prototype. var doSomething = funkce())(; console.log(doSomething.prototype);

Jak je vidět výše, doSomething() má výchozí vlastnost prototypu, jak ukazuje konzole. Po spuštění tohoto kódu by konzole měla zobrazit objekt, který vypadá podobně jako tento.

( konstruktor: ƒ doSomething(), __proto__: ( konstruktor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ propertyIsEnumerable(), toLocaleLocal toString toString: ƒString (), valueOf: ƒ valueOf() ) )

K prototypu doSomething() můžeme přidat vlastnosti, jak je ukázáno níže.

Funkce doSomething()() doSomething.prototype.foo = "bar"; console.log(doSomething.prototype);

Výsledkem je:

( foo: "bar", konstruktor: ƒ doSomething(), __proto__: ( konstruktor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), propertyIsEnumerable: ƒ to propertyIsEnumerable(LeLocal), toLocal ), toString: ƒ toString(), valueOf: ƒ valueOf() ) )

Nyní můžeme použít operátor new k vytvoření instance doSomething() založené na tomto prototypu. Chcete-li použít operátor new, jednoduše zavolejte funkci normálně, kromě předpony new . Volání funkce s operátorem new vrátí objekt, který je instancí funkce. K tomuto objektu lze poté přidat vlastnosti.

Zkuste následující kód:

Funkce doSomething()() doSomething.prototype.foo = "bar"; // přidání vlastnosti do prototypu var doSomeInstancing = new doSomething(); doSomeInstancing.prop = "nějaká hodnota"; // přidání vlastnosti do objektu console.log(doSomeInstancing);

Výsledkem je výstup podobný následujícímu:

( prop: "nějaká hodnota", __proto__: ( foo: "bar", konstruktor: ƒ doSomething(), __proto__: ( konstruktor: ƒ Object(), hasOwnProperty: ƒ hasOwnProperty(), isPrototypeOf: ƒ isPrototypeOf(), vlastnostIsEnumerable ƒ propertyIsEnumerable(), toLocaleString: ƒ toLocaleString(), toString: ƒ toString(), valueOf: ƒ valueOf() ) ) )

Jak je vidět výše, __proto__ doSomeInstancing je doSomething.prototype . Ale co to dělá? Když přistoupíte k vlastnosti doSomeInstancing , prohlížeč nejprve zkontroluje, zda doSomeInstancing tuto vlastnost má.

Pokud doSomeInstancing vlastnost nemá, prohlížeč hledá vlastnost v __proto__ doSomeInstancing (také znám jako doSomething.prototype). Pokud __proto__ doSomeInstancing hledaná vlastnost, pak se použije tato vlastnost na __proto__ doSomeInstancing.

V opačném případě, pokud __proto__ doSomeInstancing vlastnost nemá, pak se pro tuto vlastnost zkontroluje __proto__ __proto__ doSomeInstancing. Ve výchozím nastavení je vlastnost prototypu jakékoli funkce __proto__ window.Object.prototype . Takže se prohlédne __proto__ __proto__ doSomeInstancing (také znám jako __proto__ doSomething.prototype (také znám jako Object.prototype pro vlastnost)). je hledaný.

Pokud vlastnost není nalezena v __proto__ __proto__ doSomeInstancing, pak se prohledá __proto__ __proto__ __proto__ doSomeInstancing. Vyskytl se však problém: __proto__ __proto__ __proto__ doSomeInstancing neexistuje. Teprve poté, poté, co je prohlédnut celý prototypový řetězec __proto__ "s a již neexistují žádné __proto__ s, prohlížeč tvrdí, že vlastnost neexistuje a dojde k závěru, že hodnota vlastnosti není definována.

Zkusme do konzole zadat další kód:

Funkce doSomething()() doSomething.prototype.foo = "bar"; var doSomeInstancing = new doSomething(); doSomeInstancing.prop = "nějaká hodnota"; console.log("doSomeInstancing.prop: " + doSomeInstancing.prop); console.log("doSomeInstancing.foo: " + doSomeInstancing.foo); console.log("doSomething.prop: " + doSomething.prop); console.log("doSomething.foo: " + doSomething.foo); console.log("doSomething.prototype.prop: " + doSomething.prototype.prop); console.log("doSomething.prototype.foo: " + doSomething.prototype.foo);

Výsledkem je následující:

DoSomeInstancing.prop: nějaká hodnota doSomeInstancing.foo: bar doSomething.prop: nedefinovaný doSomething.foo: nedefinovaný doSomething.prototype.prop: nedefinovaný doSomething.prototype.foo: bar

Různé způsoby vytváření objektů a výsledný řetězec prototypů Objekty vytvořené pomocí konstrukcí syntaxe var o = (a: 1); // Nově vytvořený objekt o má Object.prototype jako [] // o nemá žádnou vlastní vlastnost s názvem "hasOwnProperty" // hasOwnProperty je vlastní vlastností Object.prototype. // Takže o zdědí hasOwnProperty z Object.prototype // Object.prototype má jako prototyp hodnotu null. // o ---> Object.prototype ---> null var b = ["yo", "whadup", "?"]; // Pole dědí z Array.prototype // (který má metody indexOf, forEach atd.) // Řetězec prototypu vypadá takto: // b ---> Array.prototype ---> Object.prototype ---> null function f() ( return 2; ) // Funkce dědí z Function.prototype // (která má metody volání, vazbu atd.) // f ---> Function.prototype ---> Object.prototype -- -> null S konstruktorem

"Konstruktor" v JavaScriptu je "jen" funkce, která se náhodou volá operátorem new .

Funkce Graph() ( this.vertices = ; this.edges = ; ) Graph.prototype = ( addVertex: function(v) ( this.vertices.push(v); ) ); var g = new Graph(); // g je objekt s vlastními vlastnostmi "vrcholy" a "hrany". // g.[] je hodnota Graph.prototype, když je spuštěn nový Graph().

S Object.create "použít přísné"; class Polygon ( konstruktor(výška, šířka) ( this.height = výška; this.width = šířka; ) ) class Square extends Polygon ( konstruktor(stranaDélka) ( super(délka strany, Délka strany); ) get area() ( return this. výška * this.width ) set sideLength(newLength) ( this.height = newLength; this.width = newLength; ) var square = new Square(2); Výkon

Doba vyhledávání vlastností, které jsou vysoko v řetězci prototypu, může mít negativní dopad na výkon, a to může být významné v kódu, kde je výkon kritický. Navíc pokus o přístup k neexistujícím vlastnostem vždy projde celým řetězcem prototypu.

Také při iteraci vlastností objektu bude vyčíslena každá vyčíslitelná vlastnost, která je v řetězci prototypu. Chcete-li zkontrolovat, zda má objekt definovanou vlastnost sám a ne někde na jeho prototypovém řetězci, je nutné použít metodu hasOwnProperty, kterou všechny objekty dědí z Object.prototype . Abychom vám dali konkrétní příklad, ukažme si výše uvedený příklad kódu grafu, který jej ilustruje:

Console.log(g.hasOwnProperty("vertices")); // true console.log(g.hasOwnProperty("nope")); // false console.log(g.hasOwnProperty("addVertex")); // false console.log(g.__proto__.hasOwnProperty("addVertex")); // pravda

hasOwnProperty je jediná věc v JavaScriptu, která se zabývá vlastnostmi a neprochází řetězcem prototypů.

Poznámka: Nestačí zkontrolovat, zda vlastnost není definována . Vlastnost může velmi dobře existovat, ale její hodnota je náhodou nastavena na undefined .

Špatná praxe: Rozšíření nativních prototypů

Jednou z často používaných chyb je rozšíření Object.prototype nebo jednoho z dalších vestavěných prototypů.

Tato technika se nazývá opičí záplatování a přestávky zapouzdření. I když je používají populární frameworky, jako je Prototype.js, stále neexistuje dobrý důvod pro zahlcování vestavěných typů dalšími nestandardní funkčnost.

Jediným dobrým důvodem pro rozšíření vestavěného prototypu je backportování funkcí novějších enginů JavaScript, jako je Array.forEach .

Shrnutí metod pro rozšíření řetězce prototypů

Zde jsou všechny 4 způsoby a jejich výhody/proti. Všechny níže uvedené příklady vytvářejí přesně stejný výsledný objekt inst (tedy zaznamenávají stejné výsledky do konzoly), s výjimkou odlišných způsobů pro účely ilustrace.

Jméno Příklad(y) Pro(s) Nevýhody
Nová inicializace function foo()() foo.prototype = ( foo_prop: "foo val" ); function bar()() var proto = new foo; proto.bar_prop = "bar val"; bar.prototyp = proto; var inst = nová lišta; console.log(inst.foo_prop); console.log(inst.bar_prop); Podporováno v každém představitelném prohlížeči (podpora sahá až k IE 5.5!). Je také velmi rychlý, velmi standardní a velmi dobře optimalizovaný pro JIST. Aby bylo možné použít tuto metodu, musí být daná funkce inicializována. Během této inicializace může konstruktor ukládat jedinečné informace, které musí být vygenerovány pro každý objekt. Tyto jedinečné informace by však byly generovány pouze jednou, což by mohlo vést k problémům. Navíc inicializace konstruktoru může do objektu vložit nežádoucí metody. Obojí však obecně nejsou vůbec problémy (ve skutečnosti obvykle prospěšné), pokud je to celý váš vlastní kód a víte, co kde co dělá.
Object.create function foo()() foo.prototype = ( foo_prop: "foo val" ); function bar()() var proto = Object.create(foo.prototype); proto.bar_prop = "bar val"; bar.prototyp = proto; var inst = nová lišta; console.log(inst.foo_prop); console.log(inst.bar_prop); function foo()() foo.prototype = ( foo_prop: "foo val" ); function bar()() var proto = Object.create(foo.prototype, ( bar_prop: ( hodnota: "bar val" ) )); bar.prototyp = proto; var inst = nová lišta; console.log(inst.foo_prop); console.log(inst.bar_prop) Podpora ve všech dnes používaných prohlížečích, což jsou všechny prohlížeče jiných výrobců než Microsoft plus IE9 a vyšší. Umožňuje přímé nastavení __proto__ způsobem, který je pouze jednorázový, takže prohlížeč může objekt lépe optimalizovat. Umožňuje také vytváření objektů bez prototypu pomocí Object.create(null) .

Není podporováno v IE8 a nižších. Protože však společnost Microsoft ukončila rozšířenou podporu pro systémy s těmito starými prohlížeči, nemělo by to být pro většinu aplikací problémem. Navíc pomalá inicializace objektu může být výkonová černá díra, pokud použijete druhý argument, protože každá vlastnost deskriptoru objektu má svůj vlastní samostatný objekt deskriptoru. Při práci se stovkami tisíc deskriptorů objektů ve formě objektu může se zpožděním nastat vážný problém.

Object.setPrototypeOf function foo()() foo.prototype = ( foo_prop: "foo val" ); function bar()() var proto = ( bar_prop: "bar val" ); Object.setPrototypeOf(proto, foo.prototype); bar.prototyp = proto; var inst = nová lišta; console.log(inst.foo_prop); console.log(inst.bar_prop); Měl by být podceňovaný a nevýkonný. Urychlení běhu Javascriptu je zcela vyloučeno, pokud se to odvážíte použít ve finálním produkčním kódu, protože mnoho prohlížečů optimalizuje prototyp a snaží se uhodnout umístění metody v paměti při volání instance předem, ale nastavení prototypu dynamicky narušuje všechny tyto optimalizace a může dokonce donutit některé prohlížeče, aby znovu zkompilovaly váš kód pro deoptimalizaci, jen aby fungoval podle specifikací. Není podporováno v IE8 a nižších.
__proto__ function foo()() foo.prototype = ( foo_prop: "foo val" ); function bar()() var proto = ( bar_prop: "bar val", __proto__: foo.prototype ); bar.prototyp = proto; var inst = nová lišta; console.log(inst.foo_prop); console.log(inst.bar_prop); var inst = ( __proto__: ( bar_prop: "bar val", __proto__: ( foo_prop: "foo val", __proto__: Object.prototype ) ) ); console.log(inst.foo_prop); console.log(inst.bar_prop) Podpora ve všech dnes používaných prohlížečích, což jsou všechny prohlížeče jiných výrobců než Microsoft plus IE11 a vyšší. Nastavení __proto__ na něco, co není objekt, selže pouze tiše. Nevyvolá výjimku.
Hrubě zastaralé a nevýkonné. Urychlení běhu Javascriptu je zcela vyloučeno, pokud se to odvážíte použít ve finálním produkčním kódu, protože mnoho prohlížečů optimalizuje prototyp a snaží se uhodnout umístění metody v paměti při volání instance předem, ale nastavení prototypu dynamicky narušuje všechny tyto optimalizace a může dokonce donutit některé prohlížeče, aby znovu zkompilovaly váš kód pro deoptimalizaci, jen aby fungoval podle specifikací. Není podporováno v IE10 a nižších.

prototyp a Object.getPrototypeOf

Pravděpodobně jste si již všimli, že naše funkce A má speciální vlastnost zvanou prototyp . Tato speciální vlastnost funguje s operátorem JavaScript new. Odkaz na objekt prototypu se zkopíruje do vnitřní vlastnosti [] nové instance. Například, když uděláte var a1 = new A() , JavaScript (po vytvoření objektu v paměti a před spuštěním funkce A() s tímto definovaným) nastaví a1.[] = A.prototype . Když potom přistoupíte k vlastnostem instance, JavaScript nejprve zkontroluje, zda existují přímo na daném objektu, a pokud ne, vyhledá v [] . To znamená, že všechny věci, které definujete v prototypu, jsou efektivně sdíleny všemi instancemi a můžete dokonce později změnit části prototypu a nechat se změny objevit ve všech existujících instancích, pokud chcete.

Pokud ve výše uvedeném příkladu uděláte var a1 = new A(); var a2 = new A(); pak a1.doSomething by ve skutečnosti odkazovalo na Object.getPrototypeOf(a1).doSomething , což je stejné jako A.prototype.doSomething, které jste definovali, tj. Object.getPrototypeOf(a1).doSomething == Object.getPrototypeOf(a2).doSomething == A.prototype.doSomething .

Stručně řečeno, prototyp je pro typy, zatímco Object.getPrototypeOf() je stejný pro instance.

[] se dívá na rekurzivně, tj. a1.doSomething , Object.getPrototypeOf(a1).doSomething , Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething atd., dokud nebude nalezen nebo Object.getPrototypeOf nevrátí hodnotu null.

Takže když zavoláš

Var o = new Foo();

JavaScript vlastně prostě dělá

Var o = new Object(); o.[] = Foo.prototype; Foo.call(o);

(nebo něco takového) a když to uděláte později

O.someProp;

kontroluje, zda o má vlastnost someProp . Pokud ne, zkontroluje Object.getPrototypeOf(o).someProp , a pokud neexistuje, zkontroluje Object.getPrototypeOf(Object.getPrototypeOf(o)).someProp a tak dále.

Na závěr

Před napsáním složitého kódu, který jej využívá, je nezbytné porozumět prototypovému modelu dědičnosti. Uvědomte si také délku prototypových řetězců ve vašem kódu a v případě potřeby je rozdělte, abyste se vyhnuli možným problémům s výkonem. Dále by nativní prototypy nikdy neměly být rozšiřovány, pokud to není z důvodu kompatibility s novějšími funkcemi JavaScriptu.




Nahoru