Come posso dichiarare un namespace in JavaScript?

Come posso creare uno spazio dei nomi in JavaScript in modo che i miei oggetti e le mie funzioni non vengano sovrascritti da altri oggetti e funzioni con lo stesso nome? Ho usato il seguente:

if (Foo == null || typeof(Foo) != "object") { var Foo = new Object();} 

C’è un modo più elegante o sintetico di farlo?

Mi piace questo:

 var yourNamespace = { foo: function() { }, bar: function() { } }; ... yourNamespace.foo(); 

Io uso l’approccio trovato sul sito jQuery Enterprise :

Ecco il loro esempio che mostra come dichiarare proprietà e funzioni private e pubbliche. Tutto è fatto come una funzione anonima autoeseguibile.

 (function( skillet, $, undefined ) { //Private Property var isHot = true; //Public Property skillet.ingredient = "Bacon Strips"; //Public Method skillet.fry = function() { var oliveOil; addItem( "\t\n Butter \n\t" ); addItem( oliveOil ); console.log( "Frying " + skillet.ingredient ); }; //Private Method function addItem( item ) { if ( item !== undefined ) { console.log( "Adding " + $.trim(item) ); } } }( window.skillet = window.skillet || {}, jQuery )); 

Quindi, se vuoi accedere a uno dei membri pubblici, vai su skillet.fry() o skillet.ingredients .

Quello che è veramente interessante è che ora puoi estendere lo spazio dei nomi usando la stessa syntax esatta.

 //Adding new Functionality to the skillet (function( skillet, $, undefined ) { //Private Property var amountOfGrease = "1 Cup"; //Public Method skillet.toString = function() { console.log( skillet.quantity + " " + skillet.ingredient + " & " + amountOfGrease + " of Grease" ); console.log( isHot ? "Hot" : "Cold" ); }; }( window.skillet = window.skillet || {}, jQuery )); 

Il terzo argomento undefined

Il terzo argomento undefined è la fonte della variabile di valore undefined . Non sono sicuro che sia ancora pertinente oggi, ma mentre si lavora con i vecchi browser / standard JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variabile global-scope undefined è scrivibile, quindi chiunque potrebbe riscrivere il suo valore. Il terzo argomento (quando non viene passato un valore) crea una variabile denominata undefined che ha ambito per lo spazio dei nomi / funzione. Poiché non è stato passato alcun valore quando è stato creato lo spazio dei nomi, il valore predefinito è il valore undefined .

Un altro modo per farlo, che considero un po ‘meno restrittivo della forma letterale dell’object, è questo:

 var ns = new function() { var internalFunction = function() { }; this.publicFunction = function() { }; }; 

Quanto sopra è simile al modello del modulo e se ti piace o no , ti permette di esporre tutte le tue funzioni come pubbliche, evitando la rigida struttura di un object letterale.

C’è un modo più elegante o sintetico di farlo?

Sì. Per esempio:

 var your_namespace = your_namespace || {}; 

allora puoi averlo

 var your_namespace = your_namespace || {}; your_namespace.Foo = {toAlert:'test'}; your_namespace.Bar = function(arg) { alert(arg); }; with(your_namespace) { Bar(Foo.toAlert); } 

Normalmente lo costruisco in una chiusura:

 var MYNS = MYNS || {}; MYNS.subns = (function() { function privateMethod() { // Do private stuff, or build internal. return "Message"; } return { someProperty: 'prop value', publicMethod: function() { return privateMethod() + " stuff"; } }; })(); 

Il mio stile nel corso degli anni ha subito un cambiamento sottile da quando ho scritto questo, e ora mi trovo a scrivere la chiusura in questo modo:

 var MYNS = MYNS || {}; MYNS.subns = (function() { var internalState = "Message"; var privateMethod = function() { // Do private stuff, or build internal. return internalState; }; var publicMethod = function() { return privateMethod() + " stuff"; }; return { someProperty: 'prop value', publicMethod: publicMethod }; })(); 

In questo modo trovo più semplice capire l’API pubblica e l’implementazione. Pensa alla dichiarazione di reso come a un’interfaccia pubblica all’implementazione.

Poiché puoi scrivere diversi file di JavaScript e in seguito combinarli o non combinarli in un’applicazione, ognuno deve essere in grado di recuperare o build l’object dello spazio dei nomi senza danneggiare il lavoro di altri file …

Un file potrebbe voler utilizzare lo spazio namespace.namespace1 nomi namespace.namespace1 :

 namespace = window.namespace || {}; namespace.namespace1 = namespace.namespace1 || {}; namespace.namespace1.doSomeThing = function(){} 

Un altro file potrebbe voler utilizzare lo spazio namespace.namespace2 nomi namespace.namespace2 :

 namespace = window.namespace || {}; namespace.namespace2 = namespace.namespace2 || {}; namespace.namespace2.doSomeThing = function(){} 

Questi due file possono convivere o separarsi senza scontrarsi.

Ecco come Stoyan Stefanov lo fa nel suo libro JavaScript Patterns che ho trovato molto buono (mostra anche come fa commenti che consentono la documentazione dell’API generata automaticamente e come aggiungere un metodo al prototipo di un object personalizzato):

 /** * My JavaScript application * * @module myapp */ /** @namespace Namespace for MYAPP classs and functions. */ var MYAPP = MYAPP || {}; /** * A maths utility * @namespace MYAPP * @class math_stuff */ MYAPP.math_stuff = { /** * Sums two numbers * * @method sum * @param {Number} a First number * @param {Number} b Second number * @return {Number} Sum of the inputs */ sum: function (a, b) { return a + b; }, /** * Multiplies two numbers * * @method multi * @param {Number} a First number * @param {Number} b Second number * @return {Number} The inputs multiplied */ multi: function (a, b) { return a * b; } }; /** * Constructs Person objects * @class Person * @constructor * @namespace MYAPP * @param {String} First name * @param {String} Last name */ MYAPP.Person = function (first, last) { /** * First name of the Person * @property first_name * @type String */ this.first_name = first; /** * Last name of the Person * @property last_name * @type String */ this.last_name = last; }; /** * Return Person's full name * * @method getName * @return {String} First name + last name */ MYAPP.Person.prototype.getName = function () { return this.first_name + ' ' + this.last_name; }; 

Io uso questo approccio:

 var myNamespace = {} myNamespace._construct = function() { var staticVariable = "This is available to all functions created here" function MyClass() { // Depending on the class, we may build all the classs here this.publicMethod = function() { //Do stuff } } // Alternatively, we may use a prototype. MyClass.prototype.altPublicMethod = function() { //Do stuff } function privateStuff() { } function publicStuff() { // Code that may call other public and private functions } // List of things to place publically this.publicStuff = publicStuff this.MyClass = MyClass } myNamespace._construct() // The following may or may not be in another file myNamespace.subName = {} myNamespace.subName._construct = function() { // Build namespace } myNamespace.subName._construct() 

Il codice esterno può quindi essere:

 var myClass = new myNamespace.MyClass(); var myOtherClass = new myNamepace.subName.SomeOtherClass(); myNamespace.subName.publicOtherStuff(someParameter); 

Questo è il seguito del collegamento di user106826 a Namespace.js. Sembra che il progetto sia passato a GitHub . Ora è smith / namespacedotjs .

Ho usato questo semplice helper JavaScript per il mio piccolo progetto e finora sembra essere leggero ma abbastanza versatile da gestire il namespacing e il caricamento di moduli / classi. Sarebbe bello se mi permettesse di importare un pacchetto in uno spazio dei nomi a mia scelta, non solo il namespace globale … sigh, ma questo è oltre il punto.

Permette di dichiarare lo spazio dei nomi, quindi definire oggetti / moduli in quello spazio dei nomi:

 Namespace('my.awesome.package'); my.awesome.package.WildClass = {}; 

Un’altra opzione è dichiarare subito lo spazio dei nomi e il suo contenuto:

 Namespace('my.awesome.package', { SuperDuperClass: { saveTheDay: function() { alert('You are welcome.'); } } }); 

Per ulteriori esempi di utilizzo, guarda il file example.j nel sorgente .

Campione:

 var namespace = {}; namespace.module1 = (function(){ var self = {}; self.initialized = false; self.init = function(){ setTimeout(self.onTimeout, 1000) }; self.onTimeout = function(){ alert('onTimeout') self.initialized = true; }; self.init(); /* If it needs to auto-initialize, */ /* You can also call 'namespace.module1.init();' from outside the module. */ return self; })() 

Puoi opzionalmente dichiarare una variabile local , same , come self e assegnare local.onTimeout se vuoi che sia privato.

È ansible dichiarare una semplice funzione per fornire spazi dei nomi.

 function namespace(namespace) { var object = this, tokens = namespace.split("."), token; while (tokens.length > 0) { token = tokens.shift(); if (typeof object[token] === "undefined") { object[token] = {}; } object = object[token]; } return object; } // Usage example namespace("foo.bar").baz = "I'm a value!"; 

Ho creato un namespace ispirato ai moduli di Erlang. È un approccio molto funzionale, ma è così che scrivo il mio codice JavaScript in questi giorni.

Dà alla chiusura uno spazio dei nomi globale e espone una serie di funzioni definite all’interno di quella chiusura.

 (function(){ namespace("images", previous, next); // ^^ This creates or finds a root object, images, and binds the two functions to it. // It works even though those functions are not yet defined. function previous(){ ... } function next(){ ... } function find(){ ... } // A private function })(); 

Se hai bisogno dell’ambito privato:

 var yourNamespace = (function() { //Private property var publicScope = {}; //Private property var privateProperty = "aaa"; //Public property publicScope.publicProperty = "bbb"; //Public method publicScope.publicMethod = function() { this.privateMethod(); }; //Private method function privateMethod() { console.log(this.privateProperty); } //Return only the public parts return publicScope; }()); yourNamespace.publicMethod(); 

altrimenti se non userai mai l’ambito privato:

 var yourNamespace = {}; yourNamespace.publicMethod = function() { // Do something... }; yourNamespace.publicMethod2 = function() { // Do something... }; yourNamespace.publicMethod(); 

Io uso la seguente syntax per lo spazio dei nomi.

 var MYNamespace = MYNamespace|| {}; MYNamespace.MyFirstClass = function (val) { this.value = val; this.getValue = function(){ return this.value; }; } var myFirstInstance = new MYNamespace.MyFirstClass(46); alert(myFirstInstance.getValue()); 

jsfiddle: http://jsfiddle.net/rpaul/4dngxwb3/1/

Dopo aver trasferito molte delle mie librerie a diversi progetti e aver dovuto cambiare costantemente lo spazio dei nomi di livello superiore (con nome statico), ho deciso di utilizzare questa piccola funzione di supporto (open source) per definire gli spazi dei nomi.

 global_namespace.Define('startpad.base', function(ns) { var Other = ns.Import('startpad.other'); .... }); 

Descrizione dei benefici sono sul mio post del blog . Puoi prendere il codice sorgente qui .

Uno dei vantaggi che mi piace davvero è l’isolamento tra i moduli rispetto all’ordine di caricamento. È ansible fare riferimento a un modulo esterno PRIMA che sia caricato. E il riferimento all’object che ottieni sarà compilato quando il codice è disponibile.

Sono 7 anni in ritardo per la festa, ma ho fatto un bel po ‘di lavoro su questo 8 anni fa:

È importante poter creare facilmente e in modo efficiente più domini nidificati per mantenere una complessa applicazione Web organizzata e gestibile, rispettando lo spazio dei nomi globale di JavaScript (prevenendo l’inquinamento dello spazio dei nomi) e non rovinando oggetti esistenti nel percorso del namespace mentre si fa .

Da quanto sopra, questa era la mia soluzione del 2008 circa:

 var namespace = function(name, separator, container){ var ns = name.split(separator || '.'), o = container || window, i, len; for(i = 0, len = ns.length; i < len; i++){ o = o[ns[i]] = o[ns[i]] || {}; } return o; }; 

Questo non sta creando uno spazio dei nomi, ma fornisce una funzione per la creazione di spazi dei nomi.

Questo può essere condensato in un rivestimento singolo minorato:

 var namespace=function(c,f,b){var e=c.split(f||"."),g=b||window,d,a;for(d=0,a=e.length;d 

Esempio di utilizzo:

 namespace("com.example.namespace"); com.example.namespace.test = function(){ alert("In namespaced function."); }; 

O, come una dichiarazione:

 namespace("com.example.namespace").test = function(){ alert("In namespaced function."); }; 

O viene quindi eseguito come:

 com.example.namespace.test(); 

Se non hai bisogno di supporto per i browser legacy, una versione aggiornata:

 const namespace = function(name, separator, container){ var o = container || window; name.split(separator || '.').forEach(function(x){ o = o[x] = o[x] || {}; }); return o; }; 

Ora, sarei lieto di esporre lo namespace dei namespace al namespace globale stesso. (Peccato che il linguaggio di base non fornisca questo per noi!) Quindi in genere lo userei io stesso in una chiusura, come ad esempio:

 (function(){ const namespace = function(name, separator, container){ var o = container || window; name.split(separator || '.').forEach(function(x){ o = o[x] = o[x] || {}; }); return o; }; const ns = namespace("com.ziesemer.myApp"); // Optional: ns.namespace = ns; // Further extend, work with ns from here... }()); console.log("\"com\":", com); 

Il pattern Module era stato originariamente definito come un modo per fornire incapsulamento sia pubblico che privato per le classi nell’ingegneria del software convenzionale.

Quando lavoriamo con il modello del modulo, potremmo trovare utile definire un modello semplice che usiamo per iniziare con esso. Eccone uno che copre la spaziatura dei nomi, le variabili pubbliche e private.

In JavaScript, il pattern Module viene utilizzato per emulare ulteriormente il concetto di classi in modo tale che siamo in grado di includere sia metodi pubblici e privati ​​che variabili all’interno di un singolo object, proteggendo quindi parti particolari dall’ambito globale. Ciò che ne risulta è una riduzione della probabilità che i nomi delle nostre funzioni siano in conflitto con altre funzioni definite in script aggiuntivi nella pagina.

 var myNamespace = (function () { var myPrivateVar, myPrivateMethod; // A private counter variable myPrivateVar = 0; // A private function which logs any arguments myPrivateMethod = function( foo ) { console.log( foo ); }; return { // A public variable myPublicVar: "foo", // A public function utilizing privates myPublicFunction: function( bar ) { // Increment our private counter myPrivateVar++; // Call our private method using bar myPrivateMethod( bar ); } }; })(); 

vantaggi

perché il modulo è una buona scelta? Per i principianti, è molto più pulito per gli sviluppatori che provengono da uno sfondo orientato agli oggetti rispetto all’idea di incapsulamento vero, almeno da una prospettiva JavaScript.

In secondo luogo, supporta i dati privati ​​- quindi, nel modello Module, le parti pubbliche del nostro codice sono in grado di toccare le parti private, tuttavia il mondo esterno non è in grado di toccare le parti private della class.

svantaggi

Gli svantaggi del modello di modulo sono che quando accediamo a membri sia pubblici che privati ​​in modo diverso, quando desideriamo modificare la visibilità, dobbiamo effettivamente apportare modifiche a ogni posto in cui il membro è stato utilizzato.

Inoltre, non possiamo accedere ai membri privati ​​nei metodi che vengono aggiunti all’object in un secondo momento . Detto questo, in molti casi il modello di modulo è ancora abbastanza utile e, se utilizzato correttamente, ha certamente il potenziale per migliorare la struttura della nostra applicazione.

Il modello del modulo rivelatore

Ora che abbiamo un po ‘più di familiarità con il modello del modulo, diamo uno sguardo a una versione leggermente migliorata: il modello del modulo rivelatore di Christian Heilmann.

Il modello di Revealing Module è nato quando Heilmann era frustrato dal fatto che doveva ripetere il nome dell’object principale quando volevamo chiamare un metodo pubblico da un altro o accedere a variabili pubbliche. Inoltre, non era gradito il requisito del modello del modulo per dover passare per obiettare la notazione letterale per le cose che desiderava rendere pubbliche.

Il risultato dei suoi sforzi è stato un modello aggiornato in cui dovremmo semplicemente definire tutte le nostre funzioni e variabili nell’ambito privato e restituire un object anonimo con i puntatori alla funzionalità privata che desideriamo rivelare come pubblica.

Di seguito è riportato un esempio di come utilizzare il modello Revealing Module

 var myRevealingModule = (function () { var privateVar = "Ben Cherry", publicVar = "Hey there!"; function privateFunction() { console.log( "Name:" + privateVar ); } function publicSetName( strName ) { privateVar = strName; } function publicGetName() { privateFunction(); } // Reveal public pointers to // private functions and properties return { setName: publicSetName, greeting: publicVar, getName: publicGetName }; })(); myRevealingModule.setName( "Paul Kinlan" ); 

vantaggi

Questo modello consente alla syntax dei nostri script di essere più coerenti. Rende anche più chiaro alla fine del modulo quali delle nostre funzioni e variabili possono essere consultate pubblicamente, il che facilita la leggibilità.

svantaggi

Uno svantaggio di questo modello è che se una funzione privata fa riferimento a una funzione pubblica, quella funzione pubblica non può essere ignorata se è necessaria una patch. Questo perché la funzione privata continuerà a fare riferimento all’implementazione privata e il pattern non si applica ai membri pubblici, ma solo alle funzioni.

I membri di oggetti pubblici che fanno riferimento a variabili private sono anche soggetti alle note di regole senza patch sopra.

Devi controllare Namespace.js !

Il mio schema preferito è diventato ultimamente questo:

 var namespace = (function() { // expose to public return { a: internalA, c: internalC } // all private /** * Full JSDoc */ function internalA() { // ... } /** * Full JSDoc */ function internalB() { // ... } /** * Full JSDoc */ function internalC() { // ... } /** * Full JSDoc */ function internalD() { // ... } })(); 

Mi piace la soluzione di Jaco Pretorius, ma volevo rendere la parola chiave “this” un po ‘più utile puntandola all’object modulo / spazio dei nomi. La mia versione di skillet:

 (function ($, undefined) { console.log(this); }).call(window.myNamespace = window.myNamespace || {}, jQuery); 

Un bel seguito della risposta di Ionuţ G. Stan, ma che mostra i vantaggi del codice var ClassFirst = this.ClassFirst = function() {...} usando var ClassFirst = this.ClassFirst = function() {...} , che sfrutta l’ambito di chiusura di JavaScript per meno cluttering dello spazio dei nomi per le classi nello stesso spazio dei nomi.

 var Namespace = new function() { var ClassFirst = this.ClassFirst = function() { this.abc = 123; } var ClassSecond = this.ClassSecond = function() { console.log("Cluttered way to access another class in namespace: ", new Namespace.ClassFirst().abc); console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc); } } var Namespace2 = new function() { var ClassFirst = this.ClassFirst = function() { this.abc = 666; } var ClassSecond = this.ClassSecond = function() { console.log("Cluttered way to access another class in namespace: ", new Namespace2.ClassFirst().abc); console.log("Nicer way to access a class in same namespace: ", new ClassFirst().abc); } } new Namespace.ClassSecond() new Namespace2.ClassSecond() 

Produzione:

 Cluttered way to access another class in namespace: 123 Nicer way to access a class in same namespace: 123 Cluttered way to access another class in namespace: 666 Nicer way to access a class in same namespace: 666 

We can use it independently in this way:

 var A = A|| {}; AB = {}; AB = { itemOne: null, itemTwo: null, }; ABitemOne = function () { //.. } ABitemTwo = function () { //.. } 

I think you all use too much code for such a simple problem. No need to make a repo for that. Here’s a single line function.

 namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window); 

Try it :

 // --- definition --- const namespace = namespace => namespace.split(".").reduce((last, next) => (last[next] = (last[next] || {})), window); // --- Use ---- let myNamespace = namespace("abc"); myNamespace.MyClass = class MyClass {}; // --- see ---- console.log("a : ", a); 

If using a Makefile you can do this.

 // prelude.hjs billy = new ( function moduleWrapper () { const exports = this; // postlude.hjs return exports; })(); // someinternalfile.js function bob () { console.log('hi'); } exports.bob = bob; // clientfile.js billy.bob(); 

I prefer to use a Makefile anyway once I get to about 1000 lines because I can effectively comment out large swaths of code by removing a single line in the makefile. It makes it easy to fiddle with stuff. Also, with this technique the namespace only appears once in the prelude so it’s easy to change and you don’t have to keep repeating it inside the library code.

A shell script for live development in the browser when using a makefile:

 while (true); do make; sleep 1; done 

Add this as a make task ‘go’ and you can ‘make go’ to keep your build updated as you code.

I’ve written another namespacing library that works a bit more like packages / units do in other languages. It allows you to create a package of JavaScript code and the reference that package from other code:

File hello.js

 Package("hello", [], function() { function greeting() { alert("Hello World!"); } // Expose function greeting to other packages Export("greeting", greeting); }); 

File Example.js

 Package("example", ["hello"], function(greeting) { // Greeting is available here greeting(); // Alerts: "Hello World!" }); 

Only the second file needs to be included in the page. Its dependencies (file hello.js in this example) will automatically be loaded and the objects exported from those dependencies will be used to populate the arguments of the callback function.

You can find the related project in Packages JS .

My habit is to use function myName() as property storage, and then var myName as “method” holder…

Whether this is legitimate enough or not, beat me! I am relying on my PHP logic all the time, and things simply work. : D

 function myObj() { this.prop1 = 1; this.prop2 = 2; this.prop3 = 'string'; } var myObj = ( (myObj instanceof Function !== false) ? Object.create({ $props: new myObj(), fName1: function() { /* code.. */ }, fName2: function() { /* code ...*/ } }) : console.log('Object creation failed!') ); 

if (this !== that) myObj.fName1(); else myObj.fName2();

You can also do it in a ‘vice versa’ way to check before object creation which is much better :

 function myObj() { this.prop1 = 1; this.prop2 = 2; this.prop3 = 'string'; } var myObj = ( (typeof(myObj) !== "function" || myObj instanceof Function === false) ? new Boolean() : Object.create({ $props: new myObj(), init: function () { return; }, fName1: function() { /* code.. */ }, fName2: function() { /* code ...*/ } }) ); if (myObj instanceof Boolean) { Object.freeze(myObj); console.log('myObj failed!'); debugger; } else myObj.init(); 

Reference to this: JavaScript: Creating Object with Object.create()

In JavaScript there are no predefined methods to use namespaces. In JavaScript we have to create our own methods to define NameSpaces. Here is a procedure we follow in Oodles technologies.

Register a NameSpace Following is the function to register a name space

 //Register NameSpaces Function function registerNS(args){ var nameSpaceParts = args.split("."); var root = window; for(var i=0; i < nameSpaceParts.length; i++) { if(typeof root[nameSpaceParts[i]] == "undefined") root[nameSpaceParts[i]] = new Object(); root = root[nameSpaceParts[i]]; } } 

To register a Namespace just call the above function with the argument as name space separated by '.' (dot). For Example Let your application name is oodles. You can make a namespace by following method

 registerNS("oodles.HomeUtilities"); registerNS("oodles.GlobalUtilities"); var $OHU = oodles.HomeUtilities; var $OGU = oodles.GlobalUtilities; 

Basically it will create your NameSpaces structure like below in backend:

 var oodles = { "HomeUtilities": {}, "GlobalUtilities": {} }; 

In the above function you have register a namespace called "oodles.HomeUtilities" and "oodles.GlobalUtilities" . To call these namespaces we make an variable ie var $OHU and var $OGU .

These variables are nothing but an alias to Intializing the namespace. Now, Whenever you declare a function that belong to HomeUtilities you will declare it like following:

 $OHU.initialization = function(){ //Your Code Here }; 

Above is the function name initialization and it is put into an namespace $OHU . and to call this function anywhere in the script files. Just use following code.

 $OHU.initialization(); 

Similarly, with the another NameSpaces.

Spero che sia d'aiuto.