Wie deklariere ich einen Namespace in JavaScript?


990

Wie erstelle ich einen Namespace in JavaScript, damit meine Objekte und Funktionen nicht von anderen gleichnamigen Objekten und Funktionen überschrieben werden? Ich habe folgendes verwendet:

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

Gibt es eine elegantere oder prägnantere Möglichkeit, dies zu tun?


20
Ich kann sehen, wohin Sie mit der Überprüfung gehen, um festzustellen, ob der Namespace verwendet wird. Da das Objekt jedoch nicht erstellt wird, wenn dies fehlschlägt, ist es meiner Meinung nach besser, zu warnen, wenn der Namespace verwendet wird. Ehrlich gesagt sollte dies in den meisten JS-Situationen einfach nicht passieren und schnell in die Entwicklung einbezogen werden.
Annakata

18
Nehmen Sie einen "Namespace" der obersten Ebene (Fenstereigenschaft). Besitze es. Konflikte sollten frühzeitig im Test erkannt werden. Fügen Sie nicht all diese "Was wäre wenn" -Überprüfungen hinzu. Es ist ein schwerwiegendes Problem für doppelte "Namespaces" und sollte als solches behandelt werden . Sie können einem Ansatz wie jQuery folgen, um das Besetzen eines benutzerdefinierten "Namespace" zu ermöglichen. Dies ist jedoch immer noch ein Problem mit der Entwurfszeit.

Bitte ändern Sie Ihre akzeptierte Antwort in stackoverflow.com/questions/881515/… , eine viel elegantere und aktuellere Lösung.
hlfcoding

@pst was ist mit dem, was YUI macht? Ich glaube, sie tun genau dies, um ihren Namespace schrittweise zu erweitern. Tricks wie diese werden sicherlich für die Leistung in einer HTTP-Umgebung benötigt?
Simon_Weaver

Siehe auch stackoverflow.com/questions/2102591/… für Leistungsprobleme
Tim Abell

Antworten:


764

Ich mag das:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
Der wichtige Punkt ist, religiös zu sein und nicht weiter als bis zu einer Wurzelvariablen zu expandieren. Daraus muss alles fließen.
Annakata

22
Dies schafft keinen Abschluss für Ihren Code - es macht es mühsam, Ihre anderen Funktionen aufzurufen, da sie immer so aussehen müssen: yourNamespace.bar (); Ich habe NUR ein Open-Source-Projekt erstellt, um dieses Designproblem anzugehen: github.com/mckoss/namespace .
McKoss

24
annakata: "Der wichtige Punkt ist, religiös zu sein und nicht weiter als bis zu einer Wurzelvariablen zu expandieren." - Warum ist das so?
user406905

11
@alex - warum sollte es eine flache Objektstruktur geben?
Ryan

25
@ Ryan Ich meinte, dass alles unter sein sollte MyApp, zB MyApp.Views.Profile = {}eher als MyApp.users = {}und MyViews.Profile = {}. Nicht unbedingt, dass es nur zwei Ebenen Tiefe geben sollte.
Alex

1042

Ich verwende den Ansatz auf der Enterprise jQuery-Site :

Hier ist ihr Beispiel, das zeigt, wie private und öffentliche Eigenschaften und Funktionen deklariert werden. Alles wird als selbstausführende anonyme Funktion ausgeführt.

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

Wenn Sie also auf eines der öffentlichen Mitglieder zugreifen möchten, gehen Sie einfach skillet.fry()oder skillet.ingredients.

Was wirklich cool ist, ist, dass Sie den Namespace jetzt mit genau derselben Syntax erweitern können.

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

Das dritte undefinedArgument

Das dritte undefinedArgument ist die Quelle der Wertvariablen undefined. Ich bin mir nicht sicher, ob es heute noch relevant ist, aber während ich mit älteren Browsern / JavaScript-Standards (Ecmascript 5, Javascript <1.8.5 ~ Firefox 4) arbeite, ist die Variable für den globalen Bereich undefinedbeschreibbar, sodass jeder ihren Wert neu schreiben kann. Das dritte Argument (wenn kein Wert übergeben wird) erstellt eine Variable mit dem Namen, undefineddie auf den Namespace / die Funktion beschränkt ist. Da beim Erstellen des Namensraums kein Wert übergeben wurde, wird standardmäßig der Wert verwendet undefined.


9
+1 für diese großartige Probe. Für alle Interessierten war dieses Beispiel Teil von Elijah Manors ausgezeichneter Präsentation auf der Mix 2011 (ignorieren Sie den Titel) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis

11
In Elijahs Artikel sind die Vor- und Nachteile dieses Ansatzes umschrieben. Vorteile: 1. Öffentliche und private Eigenschaften und Methoden, 2. verwendet keine umständliche OLN, 3. Schützt undefinierte 4. Stellt sicher, dass $ auf jQuery verweist, 5. Namespace kann Dateien umfassen, Nachteile: Schwieriger zu verstehen als OLN
Jared Beck

4
Dies wird heute IIFE ( Sofort aufgerufener Funktionsausdruck ) genannt. Danke für deine Antwort +1!
Gustavo Gondim

20
@CpILL: Ich bin mir nicht sicher, ob es noch relevant ist, aber das dritte undefinedArgument ist die Quelle der Wertvariablen undefined. Während der Arbeit mit älteren Browsern / Javascript-Standard (Ecmascript 5, Javascript <1.8.5 ~ Firefox 4) ist die Variable für den globalen Bereich undefinedbeschreibbar, sodass jeder ihren Wert neu schreiben kann. Wenn Sie ein drittes zusätzliches Argument hinzufügen, das Sie nicht übergeben, wird es wertvoll undefined. Sie haben also einen Namespace-Bereich erstellt, undefinedder von externen Quellen nicht neu geschrieben wird.
Mrówa

4
@SapphireSun Der Vorteil von window.skillet = window.skillet || {}ist, dass mehrere Skripte sicher zum gleichen Namespace hinzugefügt werden können, wenn sie nicht im Voraus wissen, in welcher Reihenfolge sie ausgeführt werden. Dies kann hilfreich sein, wenn Sie Ihre Skripteinschlüsse willkürlich neu anordnen möchten, ohne Ihren Code zu beschädigen, oder wenn Sie Skripte asynchron mit dem Attribut async laden möchten und daher keine Garantie für die Ausführungsreihenfolge haben. Siehe stackoverflow.com/questions/6439579/…
Mark Amery

338

Eine andere Möglichkeit, die ich für etwas weniger restriktiv halte als die Objektliteralform, ist folgende:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Die oben ist ziemlich genau wie das Modul Muster und ob Sie es mögen oder nicht , es können Sie alle Ihre Funktionen als öffentlich zu exponieren, während die starre Struktur eines Objektliteral zu vermeiden.


16
1. Es gibt einen Unterschied zwischen OLN und dem Modulmuster. 2. Ich mag OLN nicht / immer /, da Sie daran denken müssen, nicht das letzte nachfolgende Komma zu setzen, und alle Ihre Attribute müssen mit einem Wert (wie null oder undefiniert) initialisiert werden. Wenn Sie Schließungen für Elementfunktionen benötigen, benötigen Sie für jede dieser Methoden kleine Funktionsfabriken. Eine andere Sache ist, dass Sie alle Ihre Kontrollstrukturen in Funktionen einschließen müssen, während das obige Formular dies nicht vorschreibt. Das heißt nicht, dass ich OLN nicht benutze, nur, dass ich es manchmal nicht mag.
Ionuț G. Stan

8
Ich mag diesen Ansatz, weil er private Funktionen, Variablen und Pseudokonstanten zulässt (dh var API_KEY = 12345;).
Lawrence Barsanti

12
Mir gefällt das besser als der durch Kommas getrennte Objektcontainer, der höher gewählt wurde. Auch im Vergleich sehe ich keine Mängel. Vermisse ich etwas
Lucent

7
JS Newbie hier ... warum muss ich nicht tippen ns().publicFunction(), das heißt ... ns.publicFunction()funktioniert.
John Kraft

14
@ John Kraft, es ist wegen des newSchlüsselworts vor dem functionSchlüsselwort notwendig . Grundsätzlich deklariert es eine anonyme Funktion (und als Funktion ist es auch ein Konstruktor) und ruft sie dann sofort als Konstruktor mit auf new. Daher ist der endgültige Wert, der darin gespeichert nswird, eine (eindeutige) Instanz dieses anonymen Konstruktors. Hoffe es macht Sinn.
Ionuț G. Stan

157

Gibt es eine elegantere oder prägnantere Möglichkeit, dies zu tun?

Ja. Zum Beispiel:

var your_namespace = your_namespace || {};

dann kannst du haben

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

1
Dies gibt mir einen Fehler in IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: your_namespace; funktioniert besser.
Montag,

9
Es sollte var your_namespace = your_namespace = your_namespace || sein {} Funktioniert in jedem Browser;)
Palo

+1 von mir! Thin One funktioniert als Antwort von Jaco Pretorius, indem eine Bibliothek auf verschiedene Dateien oder verschiedene Stellen innerhalb derselben Datei erweitert wird. Einfach brilliant!
Centurian

2
@Palo Kannst du bitte erklären, warum es so sein sollte? var your_namespace = your_namespace = your_namespace || {}
Sriram

6
Sie hätten die Möglichkeit, das Objekt your_namespace in verschiedenen js-Dateien zu erweitern. Wenn Sie var your_namespace = {} verwenden, können Sie dies nicht tun, da das Objekt von jeder Datei überschrieben wird
Alex Pacurar

93

Normalerweise baue ich es in einem Verschluss:

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

Mein Stil hat sich im Laufe der Jahre seit dem Schreiben dieses Artikels geringfügig geändert, und jetzt schreibe ich den Abschluss folgendermaßen:

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

Auf diese Weise finde ich die öffentliche API und Implementierung leichter zu verstehen. Stellen Sie sich die return-Anweisung als öffentliche Schnittstelle zur Implementierung vor.


3
Sollten Sie nicht nachsehen MYNS.subns = MYNS.subns || {}?
Mirko

Ein guter Punkt, der die Übung für die Entwickler sein sollte. Sie müssen überlegen, was zu tun ist, wenn es vorhanden ist, es ersetzen, Fehler beheben, vorhandene oder Versionsprüfung verwenden und bedingt ersetzen. Ich hatte unterschiedliche Situationen, die für jede Variante erforderlich sind. In den meisten Fällen haben Sie dies möglicherweise als Randfall mit geringem Risiko, und das Ersetzen kann von Vorteil sein. Betrachten Sie ein Schurkenmodul, das versucht hat, den NS zu entführen.
Brett Ryan

1
Eine Erläuterung dieses Ansatzes finden Sie im Buch "Speaking Javascript" auf Seite 412 unter der Überschrift "Quick and Dirty Modules".
Soferio

2
Optimierungstipp: während var foo = functionund function fooähnlich sind, privat sein; Aufgrund der dynamisch typisierten Natur von JavaScript ist letzteres etwas schneller, da einige Anweisungen in den Pipelines der meisten Interpreter übersprungen werden. Mit var foomuss das Typsystem aufgerufen werden, um herauszufinden, welcher Typ dieser Variablen zugewiesen ist, während mit function foodas Typsystem automatisch weiß, dass es sich um eine Funktion handelt, sodass einige Funktionsaufrufe übersprungen werden, was zu weniger Aufrufen von CPU-Anweisungen wie führt jmp, pushq, popq, usw., die zu einer kürzeren Pipeline CPU übersetzt.
Braden Best

1
@brett oops. Du hast recht. Ich dachte an eine andere Skriptsprache. Obwohl ich immer noch darauf bestehe, ist die function fooSyntax besser lesbar. Und ich mag immer noch meine Version.
Braden Best

56

Da Sie möglicherweise verschiedene JavaScript-Dateien schreiben und diese später in einer Anwendung kombinieren oder nicht kombinieren, muss jede in der Lage sein, das Namespace-Objekt wiederherzustellen oder zu erstellen, ohne die Arbeit anderer Dateien zu beschädigen ...

Eine Datei beabsichtigt möglicherweise, den Namespace zu verwenden namespace.namespace1:

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

namespace.namespace1.doSomeThing = function(){}

Eine andere Datei möchte möglicherweise den Namespace verwenden namespace.namespace2:

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

namespace.namespace2.doSomeThing = function(){}

Diese beiden Dateien können zusammen oder getrennt leben, ohne zu kollidieren.


1
Ich habe festgestellt, dass dies eine sehr nützliche Methode zum Organisieren von Client-Skripten in mehreren Dateien in großen Anwendungen ist, in denen die Funktionalität modular sein muss.
DVK


49

So macht es Stoyan Stefanov in seinem JavaScript- Musterbuch, das ich als sehr gut empfunden habe (es zeigt auch, wie er Kommentare macht, die eine automatisch generierte API-Dokumentation ermöglichen, und wie man dem Prototyp eines benutzerdefinierten Objekts eine Methode hinzufügt):

/**
* 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

Ich benutze diesen Ansatz:

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

Externer Code kann dann sein:

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

Großartiges Detail! Vielen Dank! Ich frage mich nur, wie Sie Namespace.js sehen. Ich habe es selbst nie benutzt, daher frage ich mich, ob jemand mit Ihrem Wissen / Können / Ihrer Erfahrung in Betracht ziehen würde, es zu benutzen.
John

Ich mag das! Andererseits erhalte ich in der ersten Zeile dieses externen Codes eine Ausnahme, die besagt: 'myNameSpace.MyClass' [undefined] ist kein Konstruktor. Vielleicht hängt es von der JS-Implementierung ab? : /
Yoosiba

@yossiba: Möglicherweise. Der obige Code ist ziemlich normal. In Standard-JS kann jede Funktion als Konstruktor verwendet werden. Sie müssen nichts tun, um eine Funktion als spezifisch für die Verwendung als Konstruktor zu markieren. Verwenden Sie einen ungewöhnlichen Geschmack wie ActionScript oder so?
AnthonyWJones

@Anthony es ist besser, var MYNAMESPACE = MYNAMESPACE || zu verwenden {}; Nur var myNamespace = {} zu verwenden ist unsicher und außerdem ist es besser, Ihren Namespace in Großbuchstaben zu deklarieren
Paul

9
@paul: "Besser" kann sehr subjektiv sein. Ich hasse es, Code zu lesen, der mich beschimpft, deshalb vermeide ich die Verwendung von Bezeichnern, die alle Großbuchstaben verwenden. Während ns = ns || {}es defensiver erscheinen mag, kann es zu anderen unerwarteten Ergebnissen führen.
AnthonyWJones

32

Dies ist eine Fortsetzung des Links von user106826 zu Namespace.js. Es scheint, dass das Projekt auf GitHub verschoben wurde . Es ist jetzt Smith / Namespacedotjs .

Ich habe diesen einfachen JavaScript-Helfer für mein kleines Projekt verwendet und bisher scheint er leicht und dennoch vielseitig genug zu sein, um Namespace und zu verarbeiten Laden von Modulen / Klassen . Es wäre großartig, wenn ich ein Paket in einen Namespace meiner Wahl importieren könnte, nicht nur in den globalen Namespace ... seufz, aber das ist nicht der Punkt.

Sie können den Namespace deklarieren und dann Objekte / Module in diesem Namespace definieren:

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

Eine andere Möglichkeit besteht darin, den Namespace und seinen Inhalt sofort zu deklarieren:

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

Weitere Anwendungsbeispiele finden Sie in der Datei example.js in der Quelle .


2
Solange Sie sich erinnern, hat dies einige Auswirkungen auf die Leistung, da Sie jedes Mal, wenn Sie auf my.awesome.package.WildClass zugreifen, auf die awesome-Eigenschaft von my, die package-Eigenschaft von my.awesome und die WildClass-Eigenschaft von my.awesome zugreifen. Paket.
SamStephens

29

Stichprobe:

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

Sie können optional eine localVariable deklarieren same, wie selfund zuweisen, local.onTimeoutwenn Sie möchten, dass sie privat ist.


14

Sie können eine einfache Funktion zum Bereitstellen von Namespaces deklarieren.

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

Wenn Sie den privaten Bereich benötigen:

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

Andernfalls, wenn Sie den privaten Bereich niemals verwenden werden:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();

12

Das Modulmuster wurde ursprünglich als eine Möglichkeit definiert, sowohl private als auch öffentliche Kapselung für Klassen in konventioneller Softwareentwicklung bereitzustellen.

Wenn Sie mit dem Modulmuster arbeiten, kann es hilfreich sein, eine einfache Vorlage zu definieren, die wir für den Einstieg verwenden. Hier ist eine, die Namensabstände, öffentliche und private Variablen abdeckt.

In JavaScript wird das Modulmuster verwendet, um das Konzept von Klassen weiter zu emulieren, sodass wir sowohl öffentliche / private Methoden als auch Variablen in ein einzelnes Objekt aufnehmen können, wodurch bestimmte Teile vor dem globalen Bereich geschützt werden. Dies führt zu einer Verringerung der Wahrscheinlichkeit, dass unsere Funktionsnamen mit anderen Funktionen in Konflikt stehen, die in zusätzlichen Skripten auf der Seite definiert sind.

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

    }
  };

})();

Vorteile

Warum ist das Modulmuster eine gute Wahl? Für Entwickler ist es zunächst viel sauberer, wenn sie einen objektorientierten Hintergrund haben, als die Idee einer echten Kapselung, zumindest aus JavaScript-Sicht.

Zweitens werden private Daten unterstützt. Im Modulmuster können öffentliche Teile unseres Codes die privaten Teile berühren, die Außenwelt kann jedoch die privaten Teile der Klasse nicht berühren.

Nachteile

Die Nachteile des Modulmusters bestehen darin, dass wir, da wir sowohl auf öffentliche als auch auf private Mitglieder unterschiedlich zugreifen, wenn wir die Sichtbarkeit ändern möchten, tatsächlich Änderungen an jedem Ort vornehmen müssen, an dem das Mitglied verwendet wurde.

Wir können auch nicht auf private Mitglieder in Methoden zugreifen, die dem Objekt zu einem späteren Zeitpunkt hinzugefügt werden . In vielen Fällen ist das Modulmuster jedoch immer noch sehr nützlich und kann bei korrekter Verwendung sicherlich die Struktur unserer Anwendung verbessern.

Das aufschlussreiche Modulmuster

Nachdem wir das Modulmuster etwas besser kennen, schauen wir uns eine leicht verbesserte Version an - das Heilungsmodul von Christian Heilmann.

Das Revealing Module-Muster entstand, als Heilmann frustriert war, dass er den Namen des Hauptobjekts wiederholen musste, wenn wir eine öffentliche Methode von einer anderen aufrufen oder auf öffentliche Variablen zugreifen wollten. Er mochte auch die Anforderung des Modulmusters, wechseln zu müssen, nicht wörtliche Notation für die Dinge zu beanstanden, die er öffentlich machen wollte.

Das Ergebnis seiner Bemühungen war ein aktualisiertes Muster, bei dem wir einfach alle unsere Funktionen und Variablen im privaten Bereich definieren und ein anonymes Objekt mit Zeigern auf die private Funktionalität zurückgeben, die wir als öffentlich offenbaren wollten.

Ein Beispiel für die Verwendung des Revealing Module-Musters finden Sie unten

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

Vorteile

Dieses Muster ermöglicht eine konsistentere Syntax unserer Skripte. Außerdem wird am Ende des Moduls klarer, auf welche unserer Funktionen und Variablen öffentlich zugegriffen werden kann, was die Lesbarkeit erleichtert.

Nachteile

Ein Nachteil dieses Musters besteht darin, dass, wenn sich eine private Funktion auf eine öffentliche Funktion bezieht, diese öffentliche Funktion nicht überschrieben werden kann, wenn ein Patch erforderlich ist. Dies liegt daran, dass die private Funktion weiterhin auf die private Implementierung verweist und das Muster nicht für öffentliche Mitglieder gilt, sondern nur für Funktionen.

Öffentliche Objektmitglieder, die auf private Variablen verweisen, unterliegen ebenfalls den obigen No-Patch-Regelhinweisen.


9

Ich habe einen Namespace erstellt, der von Erlangs Modulen inspiriert ist. Es ist ein sehr funktionaler Ansatz, aber so schreibe ich heutzutage meinen JavaScript-Code.

Es gibt einem Abschluss einen globalen Namespace und macht eine definierte Menge von Funktionen innerhalb dieses Abschlusses verfügbar.

(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

Nachdem ich mehrere meiner Bibliotheken in verschiedene Projekte portiert habe und ständig den Namespace der obersten Ebene (statisch benannt) ändern musste, habe ich diese kleine (Open Source) Hilfsfunktion zum Definieren von Namespaces verwendet.

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

Eine Beschreibung der Vorteile finden Sie in meinem Blogbeitrag . Den Quellcode können Sie hier abrufen .

Einer der Vorteile, die ich sehr mag, ist die Isolation zwischen Modulen in Bezug auf die Ladereihenfolge. Sie können sich auf ein externes Modul beziehen, bevor es geladen wird. Die Objektreferenz, die Sie erhalten, wird ausgefüllt, sobald der Code verfügbar ist.


1
Ich habe eine verbesserte Version (2.0) der Namespace-Bibliothek erstellt: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss

Alle Ihre Links scheinen tot zu sein
Snoob Dogg

8

Ich verwende die folgende Syntax für den Namespace.

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

Ich bin 7 Jahre zu spät zur Party, habe aber vor 8 Jahren ziemlich viel daran gearbeitet:

Es ist wichtig, in der Lage zu sein, auf einfache und effiziente Weise mehrere verschachtelte Namespaces zu erstellen, um eine komplexe Webanwendung organisiert und verwaltbar zu halten. Dabei muss der globale JavaScript-Namespace beachtet werden (um eine Verschmutzung des Namespaces zu vermeiden) und dabei keine vorhandenen Objekte im Namespace-Pfad überlastet werden .

Aus dem oben Gesagten war dies meine Lösung von ca. 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;
};

Dies erstellt keinen Namespace, bietet jedoch eine Funktion zum Erstellen von Namespaces.

Dies kann zu einem minimierten Einzeiler verdichtet werden:

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

Anwendungsbeispiel:

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

Oder als eine Aussage:

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

Beides wird dann ausgeführt als:

com.example.namespace.test();

Wenn Sie keine Unterstützung für ältere Browser benötigen, eine aktualisierte Version:

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

Jetzt wäre ich misstrauisch, mich namespacedem globalen Namespace selbst auszusetzen . (Schade, dass die Basissprache dies nicht für uns bereitstellt!) Daher würde ich dies normalerweise selbst in einem Abschluss verwenden, wie zum Beispiel:

(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 einer größeren Anwendung muss dies nur einmal zu Beginn eines Seitenladens definiert werden (für clientbasierte Webanwendungen). Zusätzliche Dateien können dann die Namespace-Funktion wiederverwenden, wenn sie beibehalten werden (oben als "optional" enthalten). Im schlimmsten Fall, wenn diese Funktion einige Male neu deklariert wird, sind es nur wenige Codezeilen und weniger, wenn sie minimiert werden.


3

Ich denke, Sie alle verwenden zu viel Code für ein so einfaches Problem. Dafür muss kein Repo gemacht werden. Hier ist eine einzeilige Funktion.

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

Versuch es :

// --- 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

Ich mag die Lösung von Jaco Pretorius, aber ich wollte das Schlüsselwort "this" ein bisschen nützlicher machen, indem ich es auf das Modul- / Namespace-Objekt zeige. Meine Version der Pfanne:

(function ($, undefined) {

    console.log(this);

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

2

Mein Lieblingsmuster ist in letzter Zeit so geworden:

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

Natürlich kann die Rückgabe am Ende erfolgen, aber wenn nur Funktionsdeklarationen folgen, ist es viel einfacher zu erkennen, worum es im Namespace geht und welche API verfügbar gemacht wird.

Das Muster der Verwendung von Funktionsausdrücken in solchen Fällen führt dazu, dass nicht bekannt ist, welche Methoden verfügbar gemacht werden, ohne den gesamten Code zu durchlaufen.


Hallo, wie rufst du öffentliche Funktionen von deinem Snippet aus auf? Ich habe versuchtnamespace.a();
Olimart

@olivier ja, das ist die Idee. Obwohl jetzt mit ES6, verwende ich normalerweise die Kurzsyntax von Objektliteralen ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed

1

Wenn Sie ein Makefile verwenden, können Sie dies tun.

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

Ich bevorzuge es sowieso, ein Makefile zu verwenden, sobald ich ungefähr 1000 Zeilen erreicht habe, da ich große Codebereiche effektiv auskommentieren kann, indem ich eine einzelne Zeile im Makefile entferne. Es macht es einfach, mit Sachen herumzuspielen. Bei dieser Technik wird der Namespace auch nur einmal im Vorspiel angezeigt, sodass er leicht geändert werden kann und Sie ihn nicht ständig im Bibliothekscode wiederholen müssen.

Ein Shell-Skript für die Live-Entwicklung im Browser bei Verwendung eines Makefiles:

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

Fügen Sie dies als Make-Task 'go' hinzu und Sie können 'make go', um Ihren Build während des Codierens auf dem neuesten Stand zu halten.


1

Eine ziemliche Fortsetzung der Antwort von Ionuț G. Stan, zeigt jedoch die Vorteile von übersichtlichem Code mithilfe von var ClassFirst = this.ClassFirst = function() {...}, wodurch das Closure Scoping von JavaScript für weniger Namespace-Unordnung für Klassen im selben Namespace genutzt wird.

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

Ausgabe:

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

Ich habe eine andere Namespace-Bibliothek geschrieben, die ein bisschen mehr wie Pakete / Einheiten in anderen Sprachen funktioniert. Sie können ein Paket mit JavaScript-Code und die Referenz dieses Pakets aus anderem Code erstellen:

Datei hello.js

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

Datei Example.js

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

Nur die zweite Datei muss auf der Seite enthalten sein. Seine Abhängigkeiten (Datei hello.js in diesem Beispiel die ) werden automatisch geladen, und die aus diesen Abhängigkeiten exportierten Objekte werden zum Auffüllen der Argumente der Rückruffunktion verwendet.

Sie finden das zugehörige Projekt in Packages JS .


1
@ peter-mortensen Waren diese Änderungen an meiner Antwort von '11 wirklich notwendig? Es ist definitiv kein Vandalismus, was Sie tun, verstehen Sie mich nicht falsch, aber sie sind sehr oberflächlich. Ich würde es vorziehen, der alleinige Autor solcher Beiträge zu bleiben, es sei denn, Sie fügen wirklich etwas Gutes hinzu.
Stijn de Witt

1

Wir können es auf diese Weise unabhängig verwenden:

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

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

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

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

0

Meine Gewohnheit ist es, die Funktion myName () als Eigenschaftsspeicher und dann var myName zu verwenden als "Methoden" zu verwenden ...

Ob das legitim genug ist oder nicht, schlag mich! Ich verlasse mich die ganze Zeit auf meine PHP-Logik und die Dinge funktionieren einfach. : 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();

Sie können dies auch "umgekehrt" tun, um vor der Objekterstellung zu überprüfen, was viel besser ist :

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

Verweis darauf: JavaScript: Objekt mit Object.create () erstellen


0

In JavaScript gibt es keine vordefinierten Methoden zur Verwendung von Namespaces. In JavaScript müssen wir unsere eigenen Methoden erstellen, um NameSpaces zu definieren. Hier ist ein Verfahren, das wir in Oodles-Technologien befolgen.

Registrieren eines Namensraums Im Folgenden finden Sie die Funktion zum Registrieren eines Namensraums

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

Um einen Namespace zu registrieren, rufen Sie einfach die obige Funktion mit dem Argument als durch '.'(Punkt) getrenntem Namensraum auf . Zum Beispiel Lassen Sie Ihren Anwendungsnamen Unmengen sein. Sie können einen Namespace mit der folgenden Methode erstellen

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

Grundsätzlich wird Ihre NameSpaces-Struktur wie unten im Backend erstellt:

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

In der obigen Funktion haben Sie einen Namespace namens "oodles.HomeUtilities"und registriert "oodles.GlobalUtilities". Um diese Namespaces aufzurufen, erstellen wir eine Variable, dh var $OHUund var $OGU.

Diese Variablen sind nichts anderes als ein Alias ​​für die Initialisierung des Namespace. Wenn Sie nun eine Funktion deklarieren, die zu HomeUtilitiesIhnen gehört, wird sie wie folgt deklariert:

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

Oben ist die Initialisierung des Funktionsnamens aufgeführt, die in einen Namespace eingefügt wird $OHU. und diese Funktion an einer beliebigen Stelle in den Skriptdateien aufzurufen. Verwenden Sie einfach den folgenden Code.

$OHU.initialization();

Ebenso mit den anderen NameSpaces.

Ich hoffe es hilft.


0

JavaScript unterstützt standardmäßig keinen Namespace. Wenn Sie also ein Element (Funktion, Methode, Objekt, Variable) erstellen, wird es global und verschmutzt den globalen Namespace. Nehmen wir ein Beispiel für die Definition von zwei Funktionen ohne Namespace.

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

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

Es wird immer die zweite Funktionsdefinition aufgerufen. In diesem Fall löst der Namespace das Problem der Namenskollision.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.