Autorem článku je Addy Osmani; originál vyšel na jeho blogu pod názvem Essential JavaScript Namespacing Patterns. Překlad vychází s autorovým souhlasem. Autor pracuje jako vývojář v AOL, podílí se na vývoji jQuery a napsal knihu „Základní návrhové vzory v JavaScriptu“.
V tomto článku si probereme středně pokročilé a pokročilé vzory a postupy pro tvoření jmenných prostorů v JavaScriptu. Začneme rovnou popisem těchto metod, protože předpokládám, že většina čtenářů zkušenosti v této oblasti má. Pokud jste v oblasti jmenných prostorů v JS nováčky a chcete se dozvědět něco víc o základech, můžete si přečíst nejprve pasáž ‚základy jmenných prostorů‘.
Jak používat jmenné prostory?
V mnohých programovacích jazycích slouží jmenné prostory k tomu, aby kód nekolidoval s jinými objekty a proměnnými v globálním jmenném prostoru. Jsou rovněž užitečné při organizaci funkčních bloků v aplikaci do snadno udržovatelných skupin, které jsou jednoznačně identifikovatelné.
V JavaScriptu je pokročilé využívání jmenných prostorů nezbytné a důležité pro zabezpečení kódu před „rozbitím“, například v případě, že některý jiný skript na stránce použije stejnou proměnnou nebo stejné jméno metody jako vy. S rostoucím počtem různých skriptů vkládaných do stránek se tento problém stává běžnějším a všichni se s ním dříve či později setkáme. Jako slušní obyvatelé globálního jmenného prostoru musíme dělat vše pro to, abychom zároveň nerozbíjeli skripty jiným vývojářům.
JavaScript nemá zabudovanou podporu pro jmenné prostory jako jiné jazyky, ale má objekty a uzávěry, které mohou být použity k dosažení stejného efektu.
Pokročilé vzory pro vytváření jmenných prostorů
V této části si projdeme některé pokročilé vzory a pomocné techniky, které se mi osvědčily při práci na velkých projektech. Měl bych poznamenat, že vás nenutím do žádného z dále popsaných postupů, pouze ukazuji způsoby, jaké se mi v praxi osvědčily.
Automatizace vnořených jmenných prostorů (nested NS)
Pravděpodobně tušíte, že vnořenými jmennými prostory vytváříme organizovanou hierarchii v aplikaci. Jako příklad takového jmenného prostoru může sloužit třeba: application.utilities.drawing.canvas.2d. V JavaScriptu vytvoříme ekvivalent takového jmenného prostoru pomocí objektového literálu:
var application = { utilities:{ drawing:{ canvas:{ 2d:{ /*...*/ } } } } };
Pane jo, to je hnusné!
Jeden z častých problémů při použití tohoto vzoru je, že každá přidaná úroveň zanoření představuje nový objekt, který musíte zapsat a správně uzávorkovat. To může v okamžiku, kdy se vaše aplikace stane složitější a začnete potřebovat víc úrovní, představovat netriviální práci.
Jak lze tento problém vyřešit líp? V knize JavaScript Patterns ukázal Stoyan Stefanov velmi chytrý postup pro automatickou definici zanořených jmenných prostorů do existující globální proměnné pomocí funkce. Ta má jeden parametr typu řetězec, tento parametr rozparsuje a automaticky rozšíří jmenný prostor o potřebné objekty.
Postup, který doporučuje, vidíte níže. Já jsem funkci upravil na generickou, aby bylo možné postup použít pro různé jmenné prostory:
// top-level namespace je vytvořen jako objektový literál var myApp = myApp || {}; // funkce pro parsování názvu jmenného prostoru a // automatické vygenerování vložených objektů function extend( ns, ns_string ) { var parts = ns_string.split('.'), parent = ns, pl, i; if (parts[0] == "myApp") { parts = parts.slice(1); } pl = parts.length; for (i = 0; i < pl; i++) { //create a property if it doesnt exist if (typeof parent[parts[i]] == 'undefined') { parent[parts[i]] = {}; } parent = parent[parts[i]]; } return parent; } // ukázka použití: // rozšíříme myApp o hluboce zanořený jmenný prostor var mod = extend(myApp, 'myApp.modules.module2'); // výsledkem by měl být správně zanořený objekt console.log(mod); // pomocný test pro ověření, zda je možné instanci mod // použít mimo prostoru myApp pro přístup // k definovanému jmennému prostoru console.log(mod == myApp.modules.module2); //true // další ukázka snadného vnořování jmenných prostorů // pomocí funkce extend extend(myApp, 'moduleA.moduleB.moduleC.moduleD'); extend(myApp, 'longer.version.looks.like.this'); console.log(myApp);
Web inspector vypíše:

Všimněte si, že tam, kde bylo předtím potřeba zapisovat explicitně různé zanořené objekty, lze nyní jednoduše napsat jeden řádek kódu. Funguje to výtečně, když potřebujete definovat pouze samotný namespace, ale v případě, kdy potřebujete definovat i funkce a vlastnosti už v době deklarace jmenného prostoru, není tohle řešení dostatečně pružné. Přesto je opravdu mocné a podobný postup užívám běžně v některých svých projektech.
Deklarace závislostí
V této sekci se podíváme na drobné rozšíření vzoru zanořených jmenných prostorů, které můžete vidět v některých aplikacích. Víme, že lokální reference na objekt může výrazně snížit čas potřebný k přístupu k těmto objektům. Pojďme to použít na případ jmenných prostorů a podívejme se, jak to vypadá v praxi:
// běžný způsob přístupu ke vnořeným jmenným prostorům myApp.utilities.math.fibonacci(25); myApp.utilities.math.sin(56); myApp.utilities.drawing.plot(98,50,60); // s lokálním odkazem Var utils = myApp.utilities, maths = utils.math, drawing = utils.drawing; // usnadnění přístupu ke jmennému prostoru maths.fibonacci(25); maths.sin(56); drawing.plot(98, 50,60); // výše uvedený postup je pozorovatelně rychlejší při srovnávání // stovek či tisíců přístupů k vnořenému namespace vs // přístupy k lokální referenci na namespace.
Práce s lokální proměnnou je skoro vždy rychlejší než odkazování na globální proměnnou nejvyšší úrovně (např. myApp). Je to nejen rychlejší, ale i vhodnější, než opakovat neustále zanořený jmenný prostor na každém řádku a u složitějších aplikací to může zlepšit čitelnost kódu.
Stoyan doporučuje deklarovat jmenné prostory, vyžadované funkcí nebo modulem, na začátku oblasti viditelnosti (scope) dané funkce/modulu (pomocí vzoru „jedna proměnná“) a volat je pomocí tohoto vzoru. Jednou z výhod tohoto postupu je zmenšení nutnosti hledat a řešit závislosti, takže snadněji vytvoříte rozšiřitelnou architekturu, kde si můžete dynamicky nahrávat moduly do jmenných prostorů, když je potřebujete.
Dle mého názoru funguje tento vzor výborně, když pracujete na úrovni modulů, kde můžete do jednoho prostoru soustředit skupinu metod. Vytváření jmenných prostorů na úrovni jednotlivých funkcí, obzvlášť v případech, kdy je patrné významné překrývání mezi prostory, je něco, čemu byste se měli vyhnout všude, kde to je možné. Místo toho definujte prostor o úroveň výš a přistupujte k nim pomocí jedné reference.
Hluboké rozšíření objektů
Alternativním postupem k automatickému vytváření jmenných prostorů je hluboké rozšíření objektů (deep object extension – analogicky k hluboké kopii). Jmenné prostory, definované pomocí objektového literálu, mohou být snadno rozšířeny (nebo sloučeny) s jinými objekty (nebo jmennými prostory) tak, že vlastnosti a funkce z obou jmenných prostorů budou dostupné pod jedním sloučeným jmenným prostorem.
Pomocí moderních JS frameworků (viz např. jQuery funkci $.extend) je tento postup snadný, ale pokud budete řešit rozšiřování objektů (jmenných prostorů) v čistém JS, pomůže vám následující rutina.
// extend.js // written by andrew dupont, optimized by addy osmani function extend(destination, source) { var toString = Object.prototype.toString, objTest = toString.call({}); for (var property in source) { if (source[property] && objTest == toString.call(source[property])) { destination[property] = destination[property] || {}; extend(destination[property], source[property]); } else { destination[property] = source[property]; } } return destination; }; console.group("objExtend namespacing tests"); // definujeme hlavní jmenný prostor var myNS = myNS || {}; // 1. rozšíříme jej o objekt "utils" extend(myNS, { utils:{ } }); console.log('test 1', myNS); //myNS.utils je teď dostupný // 2. rozšíříme jej o zanořený namespace (namespace.hello.world.wave) extend(myNS, { hello:{ world:{ wave:{ test: function(){ /*...*/ } } } } }); // ověříme, zda přímé přiřazení funguje jak má myNS.hello.test1 = 'this is a test'; myNS.hello.world.test2 = 'this is another test'; console.log('test 2', myNS); // 3. co když myNS už obsahuje stejně pojmenovanou součást // (např. 'library')? Chceme si být jisti, že při rozšiřování // nebude přepsáno nic existujícího myNS.library = { foo:function(){} }; extend(myNS, { library:{ bar:function(){ /*...*/ } } }); // ověříme, zda je rozšíření bezpečné (jak očekáváme) // myNS by nyní měl obsahovat také library.foo, library.bar console.log('test 3', myNS); // 4. co když potřebujeme jednoduchý přístup k určitému prostoru // a nechceme jeho jméno psát stále dokola? var shorterAccess1 = myNS.hello.world; shorterAccess1.test3 = "hello again"; console.log('test 4', myNS); // úspěch, myApp.hello.world.test3 je teď 'hello again' console.groupEnd();
Pokud v aplikaci používáte jQuery, můžete využít naprosto stejné možnosti rozšíření pomocí $.extend – viz příklad:
// top-level namespace var myApp = myApp || {}; // Přímo přiřadíme zanořený NS myApp.library = { foo:function(){ /*..*/} }; // hluboké rozšíření tohoto NS o jiný // pro zajímavost řekněme, že o NS se stejným jménem // ale s odlišnými funkcemi // syntax: $.extend(deep, target, object1, object2) $.extend(true, myApp, { library:{ bar:function(){ /*..*/ } } }); console.log('test', myApp); // myApp nyní obsahuje jak library.foo(), tak library.bar() // nic nebylo přepsáno, jak jsme doufali.
Pro úplnost se podívejte na ekvivalentní zápis pomocí jQuery $.extend pro ostatní příklady z této sekce.
Základy jmenných prostorů
Jmenné prostory naleznete téměř ve všech seriózních JS aplikacích. Pokud nepracujete jen s útržky kódu, je důležité, abyste se ujistili, že správně implementujete jmenné prostory – ne proto, abyste je měli, ale hlavně proto, aby váš kód fungoval a nenechal se cizím kódem rozbít. Běžné vzory, které si ukážeme, jsou:
- Jediná globální proměnná
- Zápis objektovým literálem
- Zanořené jmenné prostory (Nested namespacing)
- Bezprostředně vyvolané funkční výrazy (Immediately-invoked Function Expressions – IIFE)
- Vsouvání jmenných prostorů (Namespace injection)
1. Jediná globální proměnná
Populární vzor pro vytvoření namespace v JavaScriptu je zvolení jedné globální proměnné jako primárního referenčního objektu. Kostra takové implementace, kde vrátíme objekt s funkcemi a vlastnostmi, může vypadat takto:
var myApplication = (function(){ function(){ /*...*/ }, return{ /*...*/ } })();
Tento postup bude fungovat v určitých situacích, ale velká obtíž spočívá v nutnosti spolehnout se na to, že nikdo jiný nepoužije globální proměnnou se stejným názvem.
Možné řešení tohoto problému nastínil Peter Michaux, totiž použití prefixovaných jmenných prostorů. V podstatě to je jednoduchá myšlenka – vyberete si jedinečný prefix pro svůj jmenný prostor (v našem příkladu to bude „myApplication_“) a pak definujeme vlastnosti, metody, proměnné a další objekty s tímto prefixem:
var myApplication_propertyA = {}; var myApplication_propertyB = {}; funcion myApplication_myMethod(){ /*..*/ }
Z hlediska snížení rizika konfliktu proměnných jde o efektivní postup, ale nezapomeňme, že jedinečně pojmenovaný objekt bude mít stejný efekt. Navíc je zde problém s tím, že výsledkem může být velké množství globálních proměnných ve chvíli, kdy vaše aplikace naroste. Navíc jste hodně závislí na tom, že stejný prefix nepoužije nikdo jiný („myApplication“ tedy nebude asi nejvhodnější… pozn.aut.) Buďte tedy opatrní, pokud si tuto metodu vyberete.
Podívejte se na další Peterovy poznámky k tomuto vzoru.
2. Zápis objektovým literálem
Na zápis objektovým literálem můžeme hledět jako na objekt, složený z párů klíč:hodnota, které jsou oddělené čárkami.
var myApplication = { getInfo:function(){ /**/ }, // můžeme rozšířit náš objektový literál a připravit // další vložené objekty, které mohou obsahovat // opět cokoli: models : {}, views : { pages : {} }, collections : {} };
Můžeme si rovněž zvolit cestu přímého přidávání vlastností do jmenného prostoru:
myApplication.foo = function(){ return "bar"; } myApplication.utils = { toString:function(){ /*..*/ }, export: function(){ /*..*/ } }
Objektové literály mají výhodu v tom, že neznečišťují globální datový prostor, ale napomáhají logickému organizování kódu a parametrů. To je užitečné ve chvíli, kdy chcete vytvořit snadno čitelnou strukturu, která může být rozšířena o další vnořené úrovně. Na rozdíl od jediné globální proměnné jsou deklarace objektových literálů často spojeny s testem existence proměnné se stejným jménem, takže pravděpodobnost kolize klesá.
Kód v dalším příkladu ukazuje různé způsoby, jakými můžeme zjistit, zda proměnná (jmenný prostor) už existuje před tím, než je definována. Běžně uvidíte možnost 1, ačkoli možnosti 3 a 5 jsou o něco důkladnější a možnost 4 je považována za „best practise“.
// Nekontroluje existenci 'myApplication' v globálním // jmenném prostoru. To je špatný postup, při kterém // snadno poškodíme existující proměnnou / NS stejného jména var myApplication = {}; /* Následující možnosti kontrolují existenci proměnné či namespace. Pokud je definována, použijeme ji, jinak vytvoříme nový objektový literál myApplication. Možnost 1: var myApplication = myApplication || {}; Možnost 2 if(!MyApplication) MyApplication = {}; Možnost 3: var myApplication = myApplication = myApplication || {} Možnost 4: myApplication || (myApplication = {}); Možnost 5: var myApplication = myApplication === undefined ? {} : myApplication; */
Existuje samozřejmě spousta variant jak a kde použít objektové literály k organizaci a strukturování kódu. U menších aplikací, kde chcete vystavit vnořené API konkrétního uzavřeného modulu, lze použít následující techniku, kdy vracíme rozhraní, které mohou využít další vývojáři. Je to obdoba vzoru pro moduly, kde jádro vzoru tvoří IIFE (viz další text) a vrácené rozhraní je objektový literál:
var namespace = (function () { // definováno v lokálním scope var privateMethod1 = function () { /* ... */ } var privateMethod2 = function () { /* ... */ } var privateProperty1 = 'foobar'; return { // vrácený objekt může být zanořen // do více úrovní, ačkoli, jak tu už zaznělo, // je tento způsob vhodnější pro malé aplikace // (alespoň podle mého) publicMethod1: privateMethod1, //zanořený jmenný prostor s veřejnou vlastností properties:{ publicProperty1: privateProperty1 }, //testujeme další jmenný prostor utils:{ publicMethod2: privateMethod2 } ... } })();
Výhoda objektových literálů je, že mají velmi jednoduchou a elegantní syntax „klíč:hodnota“. Díky ní jsme schopni snadno zapouzdřit jakoukoli logiku či funkcionalitu ve své aplikaci způsobem, který je jasně oddělí od zbytku a zároveň nabídne solidní základ pro další rozšiřování.
Nevýhodou může být, že tento zápis má tendence časem narůst do obrovských nepřehledných konstrukcí. V takovém případě stojí za zvážení vnořování jmenných prostorů (viz další text), které používá rovněž objektové literály.
Tento vzor můžeme použít ve spoustě dalších situací. Kromě jmenných prostorů jím můžeme oddělit konfiguraci aplikace do jedné oblasti, v níž může být snadno modifikována, aniž by bylo potřeba hledat konstanty, rozmístěné po celém kódu. Pro takový účel jsou objektové literály jako stvořené. Příklad využití literálů pro uložení konfigurace:
var myConfig = { language: 'english', defaults: { enableGeolocation: true, enableSharing: false, maxPhotos: 20 }, theme: { skin: 'a', toolbars: { index: 'ui-navigation-toolbar', pages: 'ui-custom-toolbar' } } }
Všimněte si, že mezi objektovým literálem a standardním zápisem v JSON jsou jen mizivé syntaktické rozdíly. Pokud se z libovolného důvodu rozhodnete ukládat konfiguraci v JSON (např. pro snazší ukládání na server), můžete. Více se o objektových literálech dozvíte v článku Rebeccy Murphey.
3. Vnořené jmenné prostory
Rozšířením předchozího vzoru jsou vnořené jmenné prostory. Jde o další běžný vzor, který snižuje riziko kolize jmen, protože i když jmenný prostor již existuje, je málo pravděpodobné, že bude obsahovat stejné potomky.
Připadá vám to povědomé?
YAHOO.util.Dom.getElementsByClassName('test');
YUI framework od Yahoo používá vnořené jmenné prostory běžně; v AOL je používéme ve spoustě velkých aplikací. Ukázková implementace vnořeného jmenného prostoru může vypadat například takto:
var myApp = myApp || {}; // při definování potomků uděláme stejný test myApp.routers = myApp.routers || {}; myApp.model = myApp.model || {}; myApp.model.special = myApp.model.special || {}; // vnořené jmenné prostory mohou být tak komplexní, jak je třeba: // myApp.utilities.charting.html5.plotGraph(/*..*/); // myApp.modules.financePlanner.getSummary(); // myApp.services.social.facebook.realtimeStream.getLatest();
Stejně tak můžete vytvářet nové vnořené prostory pomocí indexovaných vlastností, např.:
myApp["routers"] = myApp["routers"] || {}; myApp["models"] = myApp["models"] || {}; myApp["controllers"] = myApp["controllers"] || {};
Obě možnosti jsou čitelné, organizované a nabízejí poměrně bezpečnou cestu, jak v aplikaci používat jmenné prostory podobným stylem, jakým se používají v jiných jazycích. Jediný opravdový problém je ten, že JS engine musí nejprve najít objekt myApp a pak se prokousávat strukturou dovnitř, dokud nenajde funkci, kterou chcete použít.
To představuje větší množství práce při hledání, i když vývojáři jako Juriy Zaytsev (Jurij Zajcev) prováděli testy výkonu v moderních JS enginech a zjistili, že rozdíly v rychlosti mezi jednoduchým jmenným prostorem a zanořeným jsou téměř neznatelné.
4. Immediately-invoked Function Expressions (IIFE)s
Vzor IIFE (bezprostředně vykonaný funkční výraz) je v podstatě nepojmenovaná funkce, která je vyvolána hned poté, co je definována. Protože jsou v JavaScriptu funkce a proměnné, definované v takovém kontextu, viditelné pouze zevnitř, můžeme tento postup použít jako spolehlivý způsob pro uchování soukromí.
To je jeden z mnoha důvodů, proč jsou IIFE populární jako postup pro zapouzdření aplikační logiky a ochranu před globálním jmenným prostorem. Pravděpodobně tento vzor znáte pod jménem self-executing (nebo self-invoked) anonymous function, ale osobně dávám přednost pojmenování, které zvolil Ben Alman, protože myslím, že je popisnější a přesnější.
Nejjednodušší verze IIFE může vypadat třeba takto:
// (anonymní) bezprostředně vyvolaný funkční výraz (function(){ /*...*/})(); // pojmenovaný bezprostředně vyvolaný funkční výraz (function foobar(){ /*..*/}()); // "self-executing function" by vypadala spíš takto, což je velký rozdíl function foobar(){ foobar(); }
Lehce rozšířená verze prvního příkladu může vypadat třeba takto:
var namespace = namespace || {}; // zde je objekt "namespace" předán jako parametr funkce // k němuž dodáme patřičné metody a vlastnosti (function( o ){ o.foo = "foo"; o.bar = function(){ return "bar"; }; })(namespace); console.log(namespace);
I když tento příklad budeme rozšiřovat o další postupy, jako jsou public/private proměnné a funkce, zůstane stále čitelný. Pojďme si ukázat větší kód:
// namespace (jméno našeho prostoru) a undefined jsou zde předávány // proto, abychom se ujistili, že 1. prostor bude modifikován lokálně // a není přepisován zvnějšku // 2. aby hodnota "undefined" byla v našem kontextu opravdu nedefinovaná. // Důvodem jsou problémy s undefined, který ve verzích před ES5 bylo možné // změnit. (function ( namespace, undefined ) { // soukromé vlastnosti var foo = "foo", bar = "bar"; // veřejné metody a vlastnosti namespace.foobar = "foobar"; namespace.sayHello = function () { speak("hello world"); }; // soukromá metoda function speak(msg) { console.log("You said: " + msg); }; // zkontrolujeme, jestli "namespace" existuje v globálním objektu window // pokud ne, přiřadíme do window.namespace // prázdný objekt }(window.namespace = window.namespace || {}); // otestujeme veřejné metody a vlastnosti console.log(namespace.foobar); // foobar namescpace.sayHello(); // hello world // přidáme nové namespace.foobar2 = "foobar"; console.log(namespace.foobar2);
Schopnost rozšiřování je samozřejmě klíčová pro jakýkoli škálovatelný vzor jmenných prostorů; s IIFE toho lze dosáhnout poměrně snadno. V následujícím příkladu předáme náš existující jmenný prostor ‚namespace‘ jako argument anonymní funkci, která jej rozšíří („dekoruje“) o další funkce:
// pojďme přidat nové funkce do jmenného prostoru (function( namespace, undefined ){ // veřejná metoda namespace.sayGoodbye = function(){ console.log(namespace.foo); console.log(namespace.bar); speak('goodbye'); } }( window.namespace = window.namespace || {}); namespace.sayGoodbye(); //goodbye
Pro tuto chvíli je to o IIFE vše. Zájemcům o další informace o tomto vzoru doporučím článek o IIFE od Bena Almada a článek Elijaha Manora o namespace patterns z C#.
5. Namespace injection
Namespace injection („vstřikování“ jmenných prostorů, analogicky k dependency injection) je další variantou vzoru IIFE, při níž „vstřikujeme“ metody a vlastnosti do určitého jmenného prostoru pomocí funkčního wrapperu, kde používáme this jako namespace proxy. Výhodou tohoto postupu je možnost snadné aplikace funkcí na více objektů nebo jmennýc prostorů, což se může hodit napříkald v případě, že přidáváme sadu metod, na nichž chceme stavět (například gettery a settery).
Nevýhodou tohoto přístupu je, že existují snazší nebo vhodnější způsoby, jak dosáhnout stejného výsledku (například slučováním jmenných prostorů nebo jejich zanořováním), které jsme si už probrali.
Ukažme si tento vzor v akci, kdy jej použijeme k rozšíření funkce dvou jmenných prostorů, jednoho definovaného (utils) a druhého, který dynamicky vytvoříme jako součást prostoru utils (nový prostor tools).
var myApp = myApp || {}; myApp.utils = {}; (function() { var val = 5; this.getValue = function() { return val; }; this.setValue = function(newVal) { val = newVal; } // přidáme také nový jmenný prostor this.tools = {}; }).apply(myApp.utils); // Přidáme novou funkci do prostoru tools // který jsme definovali výše (function(){ this.diagnose = function(){ return 'diagnosis'; } }).apply(myApp.utils.tools); // všimněte si, že stejný postup může být použitý // i u obyčejného IIFE, kdy předáme kontext jako parametr // a budeme modifikovat tento kontext namísto implicitního // 'this' // testy console.log(myApp); //the now populated namespace console.log(myApp.utils.getValue()); // test get myApp.utils.setValue(25); // test set console.log(myApp.utils.getValue()); console.log(myApp.utils.tools.diagnose());
Angus Croll před časem navrhoval myšlenku využití funkce call k přirozenému oddělení kontextu a argumentů. Tento postup může sloužit i pro vytvoření funkce, která rozšiřuje libovolné moduly nebo jmenné prostory – pro úplnost si ji ukážeme:
// definujeme prostory, využijeme je později var ns = ns || {}, ns2 = ns2 || {}; // funkce, která vytváří potřebné proměnné a metody v cizím NS var creator = function(val){ var val = val || 0; this.next = function(){ return val++ }; this.reset = function(){ val = 0; } } creator.call(ns); // nyní existuje ns.next a ns.reset creator.call(ns2, 5000); // ns2 obsahuje stejné metody // ale hodnota val je zde 5000
Tento vzor je, jak už bylo řečeno, vhodný v případech, kdy přiřazujeme podobnou sadu funkcí do více modulů či jmenných prostorů, ale já bych doporučil využívat jej tam, kde explicitní deklarace funkcí uvnitř modulu/closure s přímým přístupem nedává smysl.
Závěr
Z výše uvedeného seznamu návrhových vzorů pro jmenné prostory používám nejčastěji vnořené jmenné prostory spolu se zápisem objektovým literálem.
IIFE a jediná globální proměnná mohou fungovat dobře pro malé a střední aplikace, protože větší aplikace budou nejspíš vyžadovat strukturované jmenné prostory, kde je potřeba zvolit řešení, které je snadno čitelné a škálovatelné. V úvodu zmiňované metody takové jsou.
Rovněž doporučuji vyzkoušet si zmiňované pokročilejší metody rozšiřování jmenných prostorů, protože z vlastní zkušenosti vím, že dokáží ušetřit spoustu času při dalším vývoji.
Pozn. red.: Před časem napsal Daniel Steigerwald miniseriál o OOP v JS, který ukazuje návrhové vzory z podobné oblasti – pro řešení „tříd“ a „zapouzdření“.
Přehled komentářů