Come posso dichiarare uno spazio dei nomi in JavaScript?


990

Come faccio a 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();}

Esiste un modo più elegante o succinto di farlo?


20
Posso vedere dove stai andando con il controllo per vedere se lo spazio dei nomi è preso, ma poiché l'oggetto non verrà creato se questo fallisce, penso che l'approccio migliore sia avvisare se lo spazio dei nomi è preso. Francamente questo non dovrebbe accadere nella maggior parte delle situazioni di JS e dovrebbe essere colto rapidamente nello sviluppo.
annakata,

18
Prendi uno "spazio dei nomi" di livello superiore (proprietà della finestra). Possederlo. I conflitti dovrebbero essere rilevati all'inizio del test. Non preoccuparti di aggiungere tutti questi controlli "what if". È un problema fatale per "spazi dei nomi" duplicati e deve essere trattato come tale . Puoi seguire un approccio come jQuery per consentire di abitare uno "spazio dei nomi" personalizzato; ma questo è ancora un problema in fase di progettazione.

Cambia la tua risposta accettata a stackoverflow.com/questions/881515/… , che è una soluzione molto più elegante e aggiornata.
hlfcoding

@pst e cosa fa YUI? Credo che facciano esattamente questo per aggiungere in modo incrementale al loro spazio dei nomi. trucchi come questo sono sicuramente necessari per le prestazioni in un ambiente HTTP?
Simon_Weaver

vedi anche stackoverflow.com/questions/2102591/… per problemi di prestazioni
Tim Abell,

Risposte:


764

Mi piace questo:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
L'importante è essere religiosi riguardo all'espansione non oltre l'unica variabile radice. Tutto deve derivare da questo.
annakata,

22
Questo non crea una chiusura per il tuo codice - rende noioso chiamare le tue altre funzioni perché devono sempre apparire come: yourNamespace.bar (); Ho realizzato un progetto open source SOLO per risolvere questo problema di progettazione: github.com/mckoss/namespace .
mckoss,

24
annakata: "Il punto importante è essere religiosi riguardo all'espansione non oltre l'unica variabile radice." - Perché?
user406905

11
@alex - perché dovrebbe esserci una struttura di oggetti superficiale?
Ryan,

25
@ Ryan Volevo dire che tutto dovrebbe essere sotto MyApp, ad esempio, MyApp.Views.Profile = {}invece di MyApp.users = {}e MyViews.Profile = {}. Non necessariamente che ci dovrebbe essere solo due livelli di profondità.
alex

1042

Uso l'approccio trovato sul sito Enterprise jQuery :

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

(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 si desidera accedere a uno dei membri pubblici, basta andare skillet.fry()o skillet.ingredients.

La cosa davvero interessante è che ora puoi estendere lo spazio dei nomi usando la stessa sintassi 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 undefinedargomento

Il terzo undefinedargomento è la fonte della variabile di valore undefined. Non sono sicuro che sia ancora rilevante oggi, ma mentre si lavora con browser / standard JavaScript più vecchi (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variabile di portata globale undefinedè scrivibile, quindi chiunque potrebbe riscriverne il valore. Il terzo argomento (quando non viene passato un valore) crea una variabile denominata undefinedche è nell'ambito dello spazio dei nomi / funzione. Poiché durante la creazione dello spazio dei nomi non è stato passato alcun valore, il valore predefinito è il valore undefined.


9
+1 per questo fantastico campione. Per chiunque fosse interessato, questo esempio faceva parte dell'eccellente presentazione di Elijah Manor al Mix 2011 (ignora il titolo) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis

11
Dall'articolo di Elia, ecco i pro e i contro di questo approccio, parafrasato. Pro: 1. Proprietà e metodi pubblici e privati, 2. Non utilizza ingombranti OLN, 3. Protegge undefined 4. Assicura che $ si riferisca a jQuery, 5. Lo spazio dei nomi può estendersi ai file, Contro: Più difficile da capire di OLN
Jared Beck

4
Questo si chiama oggi IIFE ( Espressione di funzione immediatamente invocata ). Grazie per la tua risposta +1!
Gustavo Gondim,

20
@CpILL: non sono sicuro se sia ancora rilevante, ma il terzo undefinedargomento è la fonte della variabile di valore undefined. Mentre si lavora con browser / javascript precedenti (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variabile di portata globale undefinedè scrivibile, quindi chiunque può riscriverne il valore. L'aggiunta di un terzo argomento aggiuntivo che non si sta trasmettendo lo rende prezioso undefined, quindi si stava creando un ambito dello spazio dei nomi undefinedche non verrà riscritto da fonti esterne.
mrówa,

4
@SapphireSun Il vantaggio window.skillet = window.skillet || {}è che consente a più script di aggiungersi in modo sicuro allo stesso spazio dei nomi quando non sanno in anticipo in quale ordine verranno eseguiti. Ciò può essere utile se si desidera essere in grado di riordinare le inclusioni di script in modo arbitrario senza rompere il codice o se si desidera caricare gli script in modo asincrono con l'attributo asincrono e quindi non avere alcuna garanzia sull'ordine di esecuzione. Vedi stackoverflow.com/questions/6439579/…
Mark Amery il

338

Un altro modo per farlo, che considero un po 'meno restrittivo della forma letterale dell'oggetto, è questo:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Quanto sopra è praticamente simile al modello del modulo e, che ti piaccia o no , ti consente di esporre tutte le tue funzioni come pubbliche, evitando al contempo la struttura rigida di un oggetto letterale.


16
1. C'è una differenza tra OLN e il modello del modulo. 2. Non mi piace / sempre / mi piace OLN poiché devi ricordare di non mettere l'ultima virgola finale e tutti i tuoi attributi devono essere inizializzati con un valore (come null o indefinito). Inoltre, se hai bisogno di chiusure per le funzioni membro, avrai bisogno di piccole fabbriche di funzioni per ciascuno di quei metodi. Un'altra cosa è che è necessario racchiudere tutte le strutture di controllo all'interno delle funzioni, mentre la forma sopra non lo impone. Questo non vuol dire che non uso OLN, è solo che a volte non mi piace.
Ionuț G. Stan,

8
Mi piace questo approccio perché consente funzioni private, variabili e pseudo-costanti (cioè var API_KEY = 12345;).
Lawrence Barsanti,

12
Mi piace di più rispetto al contenitore di oggetti separato da virgola che è stato votato più in alto. Non vedo nemmeno carenze a confronto. Mi sto perdendo qualcosa?
Lucent,

7
JS Newbie qui ... perché non devo scrivere ns().publicFunction(), cioè ... ns.publicFunction()funziona.
John Kraft,

14
@ John Kraft, è vicino alla newparola chiave davanti alla functionparola chiave. Fondamentalmente, ciò che sta facendo è che sta dichiarando una funzione anonima (e come funzione, è anche un costruttore) e quindi la invoca immediatamente come costruttore usando new. Pertanto, il valore finale che viene archiviato all'interno nsè un'istanza (unica) di quel costruttore anonimo. Spero abbia senso.
Ionuț G. Stan,

157

Esiste un modo più elegante o succinto di farlo?

Sì. Per esempio:

var your_namespace = your_namespace || {};

allora puoi avere

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

1
questo mi dà un errore in IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: your_namespace; funziona meglio.
Giovedì

9
dovrebbe essere var your_namespace = your_namespace = your_namespace || {} Funziona in tutti i browser;)
Palo

+1 da me! Quello sottile funziona come risposta Jaco Pretorius espandendo una libreria in file diversi o in luoghi diversi all'interno dello stesso file. Semplicemente geniale!
centuriano,

2
@Palo Puoi spiegare perché dovrebbe essere così? var your_namespace = your_namespace = your_namespace || {}
Sriram,

6
avresti la possibilità di estendere l'oggetto your_namespace in diversi file js. Quando usi var your_namespace = {} non puoi farlo, poiché l'oggetto verrà sovrascritto da ciascun file
Alex Pacurar,

93

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 avuto un sottile cambiamento da quando ho scritto questo, e ora mi ritrovo 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 l'API pubblica e l'implementazione più facili da capire. Pensa alla dichiarazione di ritorno come a un'interfaccia pubblica per l'implementazione.


3
Non dovresti controllare MYNS.subns = MYNS.subns || {}??
Mirko,

Un buon punto che dovrebbe essere l'esercizio inteso dagli sviluppatori. È necessario considerare cosa fare quando esiste, sostituirlo, errore, utilizzare il controllo di versione o esistente e sostituire in modo condizionale. Ho avuto diverse situazioni che richiedono ciascuna variante. Nella maggior parte dei casi è possibile che questo sia un caso limite a basso rischio e la sostituzione può essere utile, prendere in considerazione un modulo canaglia che ha cercato di dirottare il NS.
Brett Ryan,

1
C'è una spiegazione di questo approccio nel libro "Speaking Javascript" a pagina 412 se qualcuno lo possiede, sotto la voce "Moduli rapidi e sporchi".
Soferio,

2
Consiglio di ottimizzazione: mentre var foo = functione function foosono simili, essendo privati; a causa della natura tipicamente dinamica di JavaScript, quest'ultima è leggermente più veloce in quanto salta alcune istruzioni nella maggior parte delle pipeline degli interpreti. Con var foo, il sistema di tipi deve essere invocato per scoprire quale tipo viene assegnato a detto var, mentre con function foo, il sistema di tipi sa automaticamente che è una funzione, quindi un paio di chiamate di funzioni vengono ignorate, il che si traduce in un minor numero di invocazioni di istruzioni della CPU come jmp, pushq, popq, ecc, che si traduce in una conduttura CPU più breve.
Braden Best

1
@brett oops. Hai ragione. Stavo pensando a un linguaggio di scripting diverso. Anche se insisto ancora, la function foosintassi è più leggibile. E mi piace ancora la mia versione.
Braden Best

56

Poiché puoi scrivere diversi file di JavaScript e successivamente combinarli o meno in un'applicazione, ognuno deve essere in grado di recuperare o costruire l'oggetto dello spazio dei nomi senza danneggiare il lavoro di altri file ...

Un file potrebbe voler usare lo spazio dei nomi namespace.namespace1:

namespace = window.namespace || {};
namespace.namespace1 = namespace.namespace1 || {};

namespace.namespace1.doSomeThing = function(){}

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

namespace = window.namespace || {};
namespace.namespace2 = namespace.namespace2 || {};

namespace.namespace2.doSomeThing = function(){}

Questi due file possono vivere insieme o separati senza scontrarsi.


1
Ho trovato che questo è un metodo molto utile per organizzare lo script client in più file in applicazioni di grandi dimensioni in cui la funzionalità deve essere modulare.
DVK,


49

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 API generata automaticamente e come aggiungere un metodo al prototipo di un oggetto personalizzato):

/**
* My JavaScript application
*
* @module myapp
*/

/** @namespace Namespace for MYAPP classes 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;
};

32

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 classes 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);

Grande dettaglio! Grazie! Mi chiedo solo cosa ne pensi di Namespace.js. Non l'ho mai usato da solo, quindi mi chiedo se qualcuno con le tue conoscenze / abilità / esperienza prenderebbe in considerazione di usarlo.
Giovanni,

Mi piace! D'altra parte ottengo un'eccezione sulla prima riga di questo codice esterno, dicendo: 'myNameSpace.MyClass' [undefined] non è un costruttore. forse dipende dall'implementazione di JS? : /
yoosiba,

@yossiba: possibilmente. Il codice sopra è roba abbastanza standard. In JS standard qualsiasi funzione può essere utilizzata come costruttore, non è necessario fare nulla per contrassegnare una funzione in modo specifico per essere utilizzata come costruttore. Stai usando un sapore insolito come ActionScript o qualcosa del genere?
AnthonyWJones,

@Anthony è meglio usare var MYNAMESPACE = MYNAMESPACE || {}; solo usare var myNamespace = {} non è sicuro e inoltre è meglio dichiarare il tuo spazio dei nomi in maiuscolo
paul

9
@paul: "Better" può essere abbastanza soggettivo. Odio leggere il codice che mi grida quindi evito di usare identificatori che usano tutte le maiuscole. Sebbene ns = ns || {}possa sembrare più difensivo, può portare ad altri risultati inaspettati.
AnthonyWJones,

32

Questo è il seguito del link di user106826 a Namespace.js. Sembra che il progetto sia stato spostato su GitHub . Ora è smith / namespacedotjs .

Ho usato questo semplice aiuto JavaScript per il mio piccolo progetto e finora sembra essere leggero ma abbastanza versatile da gestire lo spazio dei nomi e il caricamento di moduli / classi. Sarebbe bello se mi permettesse di importare un pacchetto in uno spazio dei nomi di mia scelta, non solo nello spazio dei nomi globale ... sospiro, ma questo è anche il punto.

Ti permette di dichiarare lo spazio dei nomi e quindi definire oggetti / moduli in quello spazio dei nomi:

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

Un'altra opzione è dichiarare contemporaneamente lo spazio dei nomi e il suo contenuto:

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

Per altri esempi di utilizzo, guarda il file example.js nel sorgente .


2
Se ricordi che ciò ha delle implicazioni in termini di prestazioni, poiché ogni volta che accedi a my.awesome.package.WildClass accedi alla fantastica proprietà di my, alla proprietà package di my.awesome e alla proprietà WildClass di my.awesome. pacchetto.
SamStephens,

29

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 facoltativamente dichiarare una localvariabile, samecome selfe assegnare local.onTimeoutse vuoi che sia privata.


14

È possibile 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!";

13

Se è necessario l'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 utilizzerai mai l'ambito privato:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();

12

Il modello del modulo era originariamente definito come un modo per fornire incapsulamento sia pubblico che privato per le classi nell'ingegneria del software convenzionale.

Quando si lavora con il modello del modulo, potrebbe essere utile definire un modello semplice che utilizziamo per iniziare con esso. Eccone uno che copre le variabili di spaziatura, pubbliche e private.

In JavaScript, il modello del modulo viene utilizzato per emulare ulteriormente il concetto di classi in modo tale da poter includere sia metodi pubblici / privati ​​che variabili all'interno di un singolo oggetto, proteggendo in tal modo parti particolari dall'ambito globale. Ciò si traduce in 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 modello del modulo è una buona scelta? Per i principianti, è molto più pulito per gli sviluppatori che provengono da un background orientato agli oggetti rispetto all'idea del vero incapsulamento, almeno dal punto di vista JavaScript.

In secondo luogo, supporta i dati privati, quindi, nel modello del modulo, 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 classe.

svantaggi

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

Inoltre, non possiamo accedere ai membri privati ​​nei metodi che verranno aggiunti all'oggetto in un secondo momento . Detto questo, in molti casi il modello del modulo è ancora abbastanza utile e, se usato correttamente, ha sicuramente il potenziale per migliorare la struttura della nostra applicazione.

Il modello di modulo rivelatore

Ora che abbiamo un po 'più familiarità con il modello del modulo, diamo un'occhiata a una versione leggermente migliorata - il modello del modulo rivelatore di Christian Heilmann.

Il modello del modulo rivelatore è nato dal momento che Heilmann era frustrato dal fatto che doveva ripetere il nome dell'oggetto principale quando volevamo chiamare un metodo pubblico da un altro o accedere a variabili pubbliche e non gli piaceva il requisito del modello modulo per dover cambiare opporsi alla notazione letterale per le cose che desiderava rendere pubbliche.

Il risultato dei suoi sforzi è stato un modello aggiornato in cui avremmo semplicemente definito tutte le nostre funzioni e variabili nell'ambito privato e restituito un oggetto anonimo con puntatori alla funzionalità privata che desideravamo rivelare come pubblica.

Di seguito è riportato un esempio di come utilizzare il modello del modulo rivelatore

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 sintassi dei nostri script di essere più coerente. Inoltre, rende più chiaro alla fine del modulo quali funzioni e variabili sono accessibili pubblicamente per facilitare la leggibilità.

svantaggi

Uno svantaggio di questo modello è che se una funzione privata fa riferimento a una funzione pubblica, tale funzione pubblica non può essere ignorata se è necessaria una patch. Questo perché la funzione privata continuerà a fare riferimento all'implementazione privata e il modello 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 sulla regola senza patch sopra.


9

Ho creato lo spazio dei nomi che si ispira ai moduli di Erlang. È un approccio molto funzionale, ma è così che scrivo il mio codice JavaScript in questi giorni.

Dà una chiusura uno spazio dei nomi globale ed espone un insieme definito di funzioni 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

})();

8

Dopo aver portato diverse delle mie librerie su progetti diversi e aver dovuto cambiare costantemente lo spazio dei nomi di livello superiore (staticamente denominato), sono passato all'utilizzo di 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');
    ....
});

La descrizione dei vantaggi è nel mio post sul blog . Puoi prendere il codice sorgente qui .

Uno dei vantaggi che mi piace molto è l'isolamento tra i moduli rispetto all'ordine di caricamento. È possibile fare riferimento a un modulo esterno PRIMA che sia caricato. E il riferimento all'oggetto che otterrai sarà compilato quando il codice sarà disponibile.


1
Ho creato una versione migliorata (2.0) della libreria dello spazio dei nomi: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss,

tutti i tuoi collegamenti sembrano morti
snoob dogg il

8

Uso la sintassi seguente 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/


8

Sono in ritardo di 7 anni alla festa, ma ho lavorato parecchio 8 anni fa:

È importante essere in grado di creare in modo semplice ed efficiente più spazi dei nomi nidificati per mantenere organizzata e gestibile un'applicazione web complessa, rispettando lo spazio dei nomi globale JavaScript (prevenendo l'inquinamento dello spazio dei nomi) e senza ostruire oggetti esistenti nel percorso dello spazio dei nomi .

Da quanto sopra, questa era la mia soluzione intorno al 2008:

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 one-liner minimizzato:

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

Esempio di utilizzo:

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

Oppure, come una sola affermazione:

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

Entrambi vengono quindi eseguiti 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 diffidente di esporre namespaceallo spazio dei nomi globale stesso. (Peccato che la lingua di base non ci fornisca questo!) Quindi di solito lo uso da solo 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);

In un'applicazione più grande, questo deve essere definito solo una volta all'inizio del caricamento di una pagina (per app Web basate su client). File aggiuntivi possono quindi riutilizzare la funzione dello spazio dei nomi se mantenuta (inclusa come "opzionale" in precedenza). Nel peggiore dei casi, se questa funzione viene ripetuta alcune volte, è solo poche righe di codice e meno se minimizzata.


3

Penso che tutti voi usiate troppo codice per un problema così semplice. Non c'è bisogno di fare un repository per quello. Ecco una funzione a linea singola.

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

Provalo :

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

// --- Use ----
let myNamespace = namespace("a.b.c");
myNamespace.MyClass = class MyClass {};

// --- see ----
console.log("a : ", a);


2

Mi piace la soluzione di Jaco Pretorius, ma volevo rendere la parola chiave "this" un po 'più utile puntandola sull'oggetto module / namespace. La mia versione di padella:

(function ($, undefined) {

    console.log(this);

}).call(window.myNamespace = window.myNamespace || {}, jQuery);

2

Il mio modello 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() {
    // ...
  }
  
})();

Ovviamente, il ritorno può essere alla fine, ma se solo le dichiarazioni di funzioni lo seguono, è molto più facile vedere di cosa tratta lo spazio dei nomi e quale API è esposta.

Il modello di utilizzo delle espressioni di funzione in questi casi si traduce nel non essere in grado di sapere quali metodi sono esposti senza andare oltre l'intero codice.


Ciao, come si chiamano le funzioni pubbliche dal proprio frammento? Ho provatonamespace.a();
olimart il

@olivier sì, questa è l'idea. Anche se ora con ES6, di solito uso la sintassi abbreviata dei letterali degli oggetti ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed

1

Se usi un Makefile puoi farlo.

// 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();

Preferisco usare un Makefile comunque una volta arrivato a circa 1000 righe perché posso effettivamente commentare ampie strisce di codice rimuovendo una singola riga nel makefile. Rende facile giocherellare con le cose. Inoltre, con questa tecnica lo spazio dei nomi appare solo una volta nel preludio, quindi è facile da cambiare e non è necessario continuare a ripeterlo all'interno del codice della libreria.

Uno script di shell per lo sviluppo live nel browser quando si utilizza un makefile:

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

Aggiungi questo come compito make 'go' e puoi 'make go' per mantenere la tua build aggiornata mentre digiti.


1

Piuttosto un seguito della risposta di Ionuț G. Stan, ma mostrando i vantaggi del codice ordinato usando var ClassFirst = this.ClassFirst = function() {...}, che sfrutta l'ambito di chiusura di JavaScript per un minore ingombro 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

1

Ho scritto un'altra libreria di namespace che funziona un po 'di più come fanno i pacchetti / unità in altre lingue. Ti consente di creare un pacchetto di codice JavaScript e il riferimento a quel pacchetto da un altro codice:

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!"
});

Solo il secondo file deve essere incluso nella pagina. Le sue dipendenze (file hello.js in questo esempio) verranno caricate automaticamente e gli oggetti esportati da tali dipendenze verranno utilizzati per popolare gli argomenti della funzione di callback.

È possibile trovare il progetto correlato in Pacchetti JS .


1
@ peter-mortensen Erano davvero necessarie queste modifiche alla mia risposta dell'11? Sicuramente non è vandalismo quello che stai facendo, non fraintendermi, ma sono molto superficiali. Preferirei rimanere l'unico autore di post come questi se non aggiungi davvero qualcosa di buono.
Stijn de Witt,

1

Possiamo usarlo indipendentemente in questo modo:

var A = A|| {};
A.B = {};

A.B = {
    itemOne: null,
    itemTwo: null,
};

A.B.itemOne = function () {
    //..
}

A.B.itemTwo = function () {
    //..
}

0

La mia abitudine è quella di utilizzare la funzione myName () come archivio di proprietà, quindi var myName come "metodo" titolare ...

Che sia abbastanza legittimo o no, picchiami! Mi affido sempre alla mia logica PHP e le cose funzionano semplicemente. : 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();

Puoi anche farlo in un modo 'viceversa' per verificare prima della creazione dell'oggetto che è molto meglio :

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();

Riferimento a questo: JavaScript: creazione di oggetto con Object.create ()


0

In JavaScript non ci sono metodi predefiniti per usare gli spazi dei nomi. In JavaScript dobbiamo creare i nostri metodi per definire NameSpaces. Ecco una procedura che seguiamo nelle tecnologie Oodles.

Registrare uno spazio dei nomi Di seguito è la funzione per registrare uno spazio dei nomi

//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]];
 }
}

Per registrare uno spazio dei nomi basta chiamare la funzione sopra con l'argomento come spazio dei nomi separato da '.'(punto). Ad esempio Lascia che il nome dell'applicazione sia oodles. Puoi creare uno spazio dei nomi seguendo il metodo

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

Fondamentalmente creerà la tua struttura NameSpaces come sotto nel backend:

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

Nella funzione sopra hai registrato uno spazio dei nomi chiamato "oodles.HomeUtilities"e "oodles.GlobalUtilities". Per chiamare questi spazi dei nomi facciamo una variabile cioè var $OHUe var $OGU.

Queste variabili non sono altro che un alias per l'Intializzazione dello spazio dei nomi. Ora, ogni volta che dichiari una funzione che ti appartiene HomeUtilitiesla dichiarerai come segue:

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

Sopra è l'inizializzazione del nome della funzione e viene inserita in uno spazio dei nomi $OHU. e chiamare questa funzione ovunque nei file di script. Usa solo il seguente codice.

$OHU.initialization();

Allo stesso modo, con un altro NameSpaces.

Spero che sia d'aiuto.


0

JavaScript non supporta lo spazio dei nomi per impostazione predefinita. Quindi, se si crea qualsiasi elemento (funzione, metodo, oggetto, variabile), diventa globale e inquina lo spazio dei nomi globale. Facciamo un esempio di definizione di due funzioni senza spazio dei nomi,

function func1() {
    console.log("This is a first definition");

}
function func1() {
    console.log("This is a second definition");
}
func1(); // This is a second definition

Chiama sempre la seconda definizione della funzione. In questo caso, lo spazio dei nomi risolverà il problema della collisione dei nomi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.