Jak zadeklarować przestrzeń nazw w JavaScript?

990

Jak utworzyć przestrzeń nazw w JavaScript, aby moje obiekty i funkcje nie były zastępowane innymi obiektami i funkcjami o tej samej nazwie? Użyłem następujących:

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

Czy można to zrobić w bardziej elegancki lub zwięzły sposób?

Scott McKenzie
źródło
20
Widzę, dokąd zmierzasz, sprawdzając, czy przestrzeń nazw jest zajęta, ale ponieważ obiekt nie zostanie utworzony, jeśli to się nie powiedzie, myślę, że lepszym rozwiązaniem jest ostrzeżenie, jeśli przestrzeń nazw jest zajęta. Szczerze mówiąc, nie powinno tak się zdarzyć w większości sytuacji JS i powinno zostać szybko przyłapane na rozwoju.
annakata
18
Weź „przestrzeń nazw” najwyższego poziomu (właściwość window). Posiadać to. Konflikty należy wykrywać na wczesnych etapach testów. Nie zawracaj sobie głowy dodawaniem wszystkich tych czeków „co jeśli”. Jest to krytyczny problem dla zduplikowanych „przestrzeni nazw” i powinien być traktowany jako taki . Możesz zastosować podejście takie jak jQuery, aby umożliwić zamieszkanie w niestandardowej „przestrzeni nazw”; ale wciąż jest to problem związany z czasem projektowania.
Zmień zaakceptowaną odpowiedź na stackoverflow.com/questions/881515/… , która jest znacznie bardziej eleganckim i zaktualizowanym rozwiązaniem.
hlfcoding
@pst co z tym, co robi YUI? Wierzę, że robią to dokładnie, aby stopniowo dodawać do swojej przestrzeni nazw. takie sztuczki są z pewnością potrzebne do działania w środowisku HTTP?
Simon_Weaver
zobacz także stackoverflow.com/questions/2102591/ ... problemy z wydajnością
Tim Abell,

Odpowiedzi:

764

Lubię to:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();
dfa
źródło
62
Ważne jest, aby być religijnym w kwestii rozwijania nie dalej niż jednej zmiennej głównej. Wszystko musi z tego wynikać.
annakata
22
Nie powoduje to zamknięcia twojego kodu - powoduje, że wywoływanie innych funkcji jest uciążliwe, ponieważ zawsze muszą one wyglądać tak: yourNamespace.bar (); Zrobiłem projekt open source JUST, aby rozwiązać ten problem projektowy: github.com/mckoss/namespace .
mckoss
24
annakata: „Ważnym punktem jest bycie religijnym, jeśli chodzi o rozwijanie nie dalej niż tylko jednej zmiennej głównej”. - Dlaczego tak jest?
user406905
11
@alex - dlaczego powinna istnieć płytka struktura obiektu?
Ryan
25
@ Ryan Miałem na myśli, że wszystko powinno być pod MyApp, np. MyApp.Views.Profile = {}Zamiast MyApp.users = {}i MyViews.Profile = {}. Niekoniecznie, że głębokość powinna wynosić tylko dwa poziomy.
alex
1042

Korzystam z podejścia znalezionego na stronie Enterprise jQuery :

Oto ich przykład pokazujący, jak zadeklarować prywatne i publiczne właściwości i funkcje. Wszystko odbywa się jako anonimowa funkcja.

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

Więc jeśli chcesz uzyskać dostęp do jednego z członków publicznych, po prostu idź skillet.fry()lub skillet.ingredients.

Naprawdę fajne jest to, że możesz teraz rozszerzyć przestrzeń nazw przy użyciu dokładnie tej samej składni.

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

Trzeci undefinedargument

Trzeci undefinedargument jest źródłem zmiennej wartości undefined. Nie jestem pewien, czy jest on nadal aktualny, ale podczas pracy ze starszymi standardami przeglądarek / JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4) zmienna o zasięgu globalnym undefinedjest zapisywalna, aby każdy mógł przepisać jej wartość. Trzeci argument (gdy nie zostanie przekazany wartość) tworzy zmienną o nazwie, undefinedktórej zakres obejmuje przestrzeń nazw / funkcję. Ponieważ podczas tworzenia przestrzeni nazw nie przekazano żadnej wartości, domyślnie jest to wartość undefined.

Jaco Pretorius
źródło
9
+1 za tę wspaniałą próbkę. Dla wszystkich zainteresowanych ta próbka była częścią doskonałej prezentacji Elijah Manor na Mix 2011 (zignoruj ​​tytuł) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis
11
W artykule Eliasza sparafrazowano zalety i wady tego podejścia. Plusy: 1. Własne i prywatne właściwości i metody, 2. nie używa uciążliwego OLN, 3. Chroni niezdefiniowane 4. Zapewnia, że ​​$ odnosi się do jQuery, 5. Przestrzeń nazw może obejmować pliki, Wady: Trudniej zrozumieć niż OLN
Jared Beck
4
Nazywa się to dzisiaj IIFE ( Natychmiastowe wywołanie funkcji ). Dzięki za odpowiedź +1!
Gustavo Gondim,
20
@CpILL: nie jestem pewien, czy nadal jest istotny, ale trzeci undefinedargument jest źródłem zmiennej wartości undefined. Podczas pracy ze starszymi standardami przeglądarek / javascript (ecmascript 5, javascript <1.8.5 ~ firefox 4) zmienna o zasięgu globalnym undefinedjest zapisywalna, więc każdy może przepisać jej wartość. Dodanie trzeciego, dodatkowego argumentu, którego nie przekazujesz, sprawia, że ​​ma on wartość undefined, więc tworzyłeś zakres nazw, undefinedktóry nie będzie przepisywany przez źródła zewnętrzne.
mrówa
4
@SapphireSun Zaletą window.skillet = window.skillet || {}jest to, że pozwala wielu skryptom bezpiecznie dodawać do tej samej przestrzeni nazw, gdy nie wiedzą z góry, w jakiej kolejności będą wykonywane. Może to być pomocne, jeśli chcesz móc dowolnie zmieniać kolejność włączeń skryptu bez zerwania kodu lub jeśli chcesz ładować skrypty asynchronicznie z atrybutem asynchronicznym, a zatem nie masz gwarancji kolejności wykonania. Zobacz stackoverflow.com/questions/6439579/…
Mark Amery
338

Innym sposobem na zrobienie tego, co uważam za nieco mniej restrykcyjne niż dosłowna forma obiektu, jest:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Powyższe jest bardzo podobne do wzoru modułu i czy ci się to podoba, czy nie , pozwala ujawnić wszystkie twoje funkcje jako publiczne, unikając sztywnej struktury dosłowności obiektu.

Ionuț G. Stan
źródło
16
1. Istnieje różnica między OLN a wzorcem modułu. 2. Nie zawsze / lubię OLN, ponieważ musisz pamiętać, aby nie wstawiać ostatniego przecinka, a wszystkie twoje atrybuty muszą być inicjowane wartością (np. Null lub undefined). Ponadto, jeśli potrzebujesz zamknięć dla funkcji składowych, będziesz potrzebował małych fabryk funkcji dla każdej z tych metod. Inną rzeczą jest to, że musisz zawrzeć wszystkie swoje struktury kontrolne wewnątrz funkcji, podczas gdy powyższa forma tego nie narzuca. Nie oznacza to, że nie używam OLN, po prostu czasami mi się nie podoba.
Ionuț G. Stan
8
Podoba mi się to podejście, ponieważ pozwala na prywatne funkcje, zmienne i pseudo-stałe (tj. Var API_KEY = 12345;).
Lawrence Barsanti
12
Podoba mi się to lepiej niż kontener obiektów oddzielonych przecinkami, który został wyżej oceniony. Nie widzę też żadnych niedociągnięć w porównaniu. Czy coś brakuje?
Lucent,
7
JS Newbie tutaj ... dlaczego nie muszę pisać ns().publicFunction(), to znaczy ... ns.publicFunction()działa.
John Kraft,
14
@John Kraft, to newsłowo kluczowe przed functionsłowem kluczowym. Zasadniczo polega na tym, że deklaruje funkcję anonimową (a jako funkcja jest również konstruktorem), a następnie natychmiast wywołuje ją jako konstruktora new. Jako taka, ostateczna wartość, która jest przechowywana w środku, nsjest (unikalną) instancją tego anonimowego konstruktora. Mam nadzieję, że to ma sens.
Ionuț G. Stan
157

Czy można to zrobić w bardziej elegancki lub zwięzły sposób?

Tak. Na przykład:

var your_namespace = your_namespace || {};

wtedy możesz mieć

var your_namespace = your_namespace || {};
your_namespace.Foo = {toAlert:'test'};
your_namespace.Bar = function(arg) 
{
    alert(arg);
};
with(your_namespace)
{
   Bar(Foo.toAlert);
}
Alex Pacurar
źródło
1
to daje mi błąd w IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: twoja_przestrzeń nazw; działa lepiej.
mjallday,
9
powinno to być var ​​two_namespace = your_namespace = your_namespace || {} Działa w każdej przeglądarce;)
Palo,
+1 ode mnie! Cienki działa jako odpowiedź Jaco Pretoriusa poprzez rozszerzenie jednej biblioteki do różnych plików lub różnych miejsc w tym samym pliku. Po prostu genialne!
centurian
2
@Palo Czy możesz wyjaśnić, dlaczego tak powinno być? var your_namespace = your_namespace = your_namespace || {}
Sriram
6
miałbyś możliwość rozszerzenia obiektu twoja_nazwa_przestrzeni w różnych plikach js. Używając var your_namespace = {} nie możesz tego zrobić, ponieważ obiekt zostanie zastąpiony przez każdy plik
Alex Pacurar
92

Zwykle buduję to w zamknięciu:

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

Mój styl na przestrzeni lat ulegał subtelnej zmianie od momentu napisania tego, a teraz zaczynam pisać w następujący sposób:

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

W ten sposób łatwiej mi zrozumieć publiczny interfejs API i implementację. Pomyśl o deklaracji return jako o publicznym interfejsie do implementacji.

Brett Ryan
źródło
3
Nie powinieneś sprawdzać MYNS.subns = MYNS.subns || {}?
Mirko,
Dobra uwaga, która powinna być ćwiczeniem dla programistów. Musisz zastanowić się, co zrobić, gdy istnieje, zastąpić go, naprawić błąd, użyć istniejącej lub sprawdzania wersji i warunkowo zastąpić. Miałem różne sytuacje, które wymagają każdego wariantu. W większości przypadków może to być przypadek o niskim ryzyku, a wymiana może być korzystna, rozważ nieuczciwy moduł, który próbował przejąć NS.
Brett Ryan,
1
Wyjaśnienie tego podejścia znajduje się w książce „Mówienie Javascript” na stronie 412, jeśli ktoś go ma, pod nagłówkiem „Szybkie i brudne moduły”.
Soferio,
2
Wskazówka optymalizacyjna: będąc var foo = functioni function foobędąc podobnymi, będąc prywatnym; ze względu na dynamicznie pisaną naturę JavaScript ten ostatni jest nieco szybszy, ponieważ pomija kilka instrukcji w potokach większości tłumaczy. Z var foo, system typów musi zostać wywołany, aby dowiedzieć się, jaki typ jest przypisany do wspomnianego var, natomiast z function foo, system typów automatycznie wie, że jest to funkcja, więc kilka wywołań funkcji jest pomijanych, co przekłada się na mniej wywołań instrukcji procesora, takich jak jmp, pushq, popq, etc, co przekłada się na krótszy rurociągu procesora.
Braden Best
1
@brett oops. Masz rację. Myślałem o innym języku skryptowym. Chociaż wciąż nalegam, aby function fooskładnia była bardziej czytelna. I nadal lubię moją wersję.
Braden Best
56

Ponieważ możesz pisać różne pliki JavaScript, a następnie łączyć lub nie łączyć ich w aplikacji, każdy musi mieć możliwość odzyskania lub skonstruowania obiektu przestrzeni nazw bez uszkadzania pracy innych plików ...

Jeden plik może używać przestrzeni nazw namespace.namespace1:

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

namespace.namespace1.doSomeThing = function(){}

Inny plik może chcieć użyć przestrzeni nazw namespace.namespace2:

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

namespace.namespace2.doSomeThing = function(){}

Te dwa pliki mogą żyć razem lub osobno, bez kolizji.

Fentex
źródło
1
Uważam, że jest to bardzo przydatna metoda organizowania skryptu klienta w wiele plików w dużych aplikacjach, w których funkcjonalność musi być modułowa.
DVK
Pytanie zadawane specjalnie dla wielu plików: stackoverflow.com/questions/5150124/...
Ciro Santilli 冠状 病毒 审查 六四 事件 法轮功
48

Oto, jak Stoyan Stefanov robi to w swojej książce Wzory JavaScript, którą uważam za bardzo dobrą (pokazuje również, jak robi komentarze, które pozwalają na automatycznie wygenerowaną dokumentację API i jak dodać metodę do prototypu obiektu niestandardowego):

/**
* 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;
};
Ciaran Bruen
źródło
32

Używam tego podejścia:

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

Kod zewnętrzny może wtedy być:

var myClass = new myNamespace.MyClass();
var myOtherClass = new myNamepace.subName.SomeOtherClass();
myNamespace.subName.publicOtherStuff(someParameter);
AnthonyWJones
źródło
Dobry szczegół! Dzięki! Zastanawiam się, jakie jest twoje zdanie na temat Namespace.js. Nigdy go nie używałem, więc zastanawiam się, czy ktoś z twoją wiedzą / umiejętnościami / doświadczeniem rozważyłby jego użycie.
John,
Lubię to! Z drugiej strony dostaję wyjątek w pierwszym wierszu tego zewnętrznego kodu, mówiąc: „mojaNameSpace.MyClass” [niezdefiniowany] nie jest konstruktorem. może zależy to od implementacji JS? : /
yoosiba
@yossiba: Możliwe. Powyższy kod jest dość standardowy. W standardowym JS dowolną funkcję można wykorzystać jako konstruktor, nie trzeba nic robić, aby oznaczyć funkcję jako specyficzną do użycia jako konstruktor. Czy używasz niezwykłego smaku, takiego jak ActionScript lub coś takiego?
AnthonyWJones
@Anthony lepiej używać var ​​MYNAMESPACE = MYNAMESPACE || {}; samo użycie var myNamespace = {} jest niebezpieczne, a ponadto lepiej zadeklarować przestrzeń nazw w wielkich literach
paul
9
@paul: „Better” może być dość subiektywny. Nienawidzę czytać kodu, który MÓWI na mnie, więc unikam używania identyfikatorów, które używają wielkich liter. Choć ns = ns || {}może wydawać się bardziej defensywny, może prowadzić do innych nieoczekiwanych rezultatów.
AnthonyWJones,
32

Jest to kontynuacja linku user106826 do Namespace.js. Wygląda na to, że projekt został przeniesiony do GitHub . Teraz jest to Smith / namespacedotjs .

Używam tego prostego pomocnika JavaScript do mojego małego projektu i do tej pory wydaje się on być lekki, ale wystarczająco wszechstronny, aby obsługiwać przestrzeń nazw i ładowanie modułów / klas. Byłoby wspaniale, gdyby pozwolił mi zaimportować pakiet do wybranej przeze mnie przestrzeni nazw, nie tylko globalnej przestrzeni nazw ... westchnienie, ale to poza tym.

Pozwala zadeklarować przestrzeń nazw, a następnie zdefiniować obiekty / moduły w tej przestrzeni nazw:

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

Inną opcją jest jednoczesne zadeklarowanie przestrzeni nazw i jej zawartości:

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

Więcej przykładów użycia znajduje się w pliku example.js w źródle .

Rudy Lattae
źródło
2
Tak długo, jak pamiętasz, ma to wpływ na wydajność, ponieważ za każdym razem, gdy uzyskujesz dostęp do my.awesome.package.WildClass, masz dostęp do niesamowitej właściwości my, właściwości package my.awesome i właściwości WildClass my.awesome. pakiet.
SamStephens,
29

Próba:

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

Możesz opcjonalnie zadeklarować localzmienną, samepolubić selfi przypisać, local.onTimeoutjeśli chcesz, aby była prywatna.

Jim Jose
źródło
14

Możesz zadeklarować prostą funkcję zapewniającą przestrzenie nazw.

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!";
dnemoga
źródło
13

Jeśli potrzebujesz zakresu prywatnego:

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

w przeciwnym razie, jeśli nigdy nie będziesz używać zakresu prywatnego:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();
Tadej
źródło
12

Wzorzec modułu został pierwotnie zdefiniowany jako sposób zapewnienia enkapsulacji zarówno prywatnej, jak i publicznej dla klas w konwencjonalnej inżynierii oprogramowania.

Podczas pracy ze wzorcem modułu może się przydać zdefiniowanie prostego szablonu, którego używamy do rozpoczęcia pracy z nim. Oto jeden, który obejmuje odstępy między nazwami, zmienne publiczne i prywatne.

W JavaScript wzorzec modułu służy do dalszej emulacji koncepcji klas w taki sposób, że jesteśmy w stanie zawrzeć zarówno metody publiczne / prywatne, jak i zmienne w jednym obiekcie, chroniąc w ten sposób poszczególne części przed zasięgiem globalnym. Powoduje to zmniejszenie prawdopodobieństwa konfliktu nazw naszych funkcji z innymi funkcjami zdefiniowanymi w dodatkowych skryptach na stronie.

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

    }
  };

})();

Zalety

dlaczego wzorzec modułu jest dobrym wyborem? Na początek jest o wiele czystszy dla programistów pochodzących z obiektowego tła niż idea prawdziwej enkapsulacji, przynajmniej z perspektywy JavaScript.

Po drugie, obsługuje dane prywatne - więc, zgodnie ze wzorem modułu, części publiczne naszego kodu mogą dotykać części prywatnych, jednak świat zewnętrzny nie jest w stanie dotknąć części prywatnych klasy.

Niedogodności

Wady wzorca modułu polegają na tym, że ponieważ w różny sposób uzyskujemy dostęp zarówno do członków publicznych, jak i prywatnych, gdy chcemy zmienić widoczność, musimy wprowadzić zmiany w każdym miejscu, w którym członek był używany.

Nie możemy również uzyskać dostępu do prywatnych członków metodami dodanymi do obiektu w późniejszym czasie . To powiedziawszy, w wielu przypadkach wzorzec modułu jest nadal bardzo przydatny, a przy prawidłowym użyciu z pewnością może poprawić strukturę naszej aplikacji.

Wzorzec modułu ujawniania

Teraz, gdy jesteśmy trochę bardziej zaznajomieni ze wzorem modułu, spójrzmy na nieco ulepszoną wersję - wzór modułu ujawnienia Christiana Heilmanna.

Wzorzec modułu ujawniania pojawił się, gdy Heilmann był sfrustrowany faktem, że musiał powtórzyć nazwę głównego obiektu, gdy chcieliśmy wywołać jedną metodę publiczną z innej lub uzyskać dostęp do zmiennych publicznych. Nie podobało mu się również wymaganie, by wzorzec modułu wymagał zmiany sprzeciwić się dosłownej notacji rzeczy, które chciał upublicznić.

Rezultatem jego wysiłków był zaktualizowany wzorzec, w którym po prostu zdefiniujemy wszystkie nasze funkcje i zmienne w zakresie prywatnym i zwrócimy anonimowy obiekt ze wskazówkami do funkcji prywatnej, którą chcieliśmy ujawnić jako publiczną.

Przykład użycia wzoru modułu ujawniania znajduje się poniżej

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

Zalety

Ten wzorzec pozwala na bardziej spójną składnię naszych skryptów. Ułatwia także na końcu modułu, do których z naszych funkcji i zmiennych można uzyskać dostęp publiczny, co ułatwia czytelność.

Niedogodności

Wadą tego wzorca jest to, że jeśli funkcja prywatna odnosi się do funkcji publicznej, tej funkcji publicznej nie można zastąpić, jeśli łatka jest konieczna. Wynika to z faktu, że funkcja prywatna będzie nadal odnosić się do prywatnej implementacji, a wzorzec nie dotyczy członków publicznych, a jedynie funkcji.

Członkowie obiektów publicznych, którzy odnoszą się do zmiennych prywatnych, podlegają również powyższym uwagom dotyczącym zasady no-patch.

Divyanshu Rawat
źródło
9

Stworzyłem przestrzeń nazw inspirowaną modułami Erlanga. Jest to bardzo funkcjonalne podejście, ale w ten sposób piszę teraz mój kod JavaScript.

Nadaje zamknięciu globalną przestrzeń nazw i udostępnia określone funkcje zestawu w tym zamknięciu.

(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

})();
WHO
źródło
8

Po przeniesieniu kilku moich bibliotek do różnych projektów i konieczności ciągłej zmiany przestrzeni nazw najwyższego poziomu (o nazwie statycznej), przerzuciłem się na tę małą funkcję pomocniczą (open source) do definiowania przestrzeni nazw.

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

Opis korzyści znajduje się na moim blogu . Możesz pobrać kod źródłowy tutaj .

Jedną z korzyści, które naprawdę lubię, jest izolacja między modułami w odniesieniu do kolejności ładowania. Możesz odwołać się do zewnętrznego modułu PRZED załadowaniem. Otrzymane odwołanie do obiektu zostanie wypełnione, gdy kod będzie dostępny.

McKoss
źródło
1
Stworzyłem ulepszoną wersję (2.0) biblioteki przestrzeni nazw: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss
wszystkie twoje linki wydają się martwe
snoob dogg
8

Używam następującej składni dla przestrzeni nazw.

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/

Razan Paul
źródło
8

Jestem 7 lat spóźniony na imprezę, ale wykonałem sporo pracy około 8 lat temu:

Ważne jest, aby móc łatwo i wydajnie tworzyć wiele zagnieżdżonych przestrzeni nazw, aby utrzymać złożoną aplikację internetową zorganizowaną i zarządzaną, przy jednoczesnym poszanowaniu globalnej przestrzeni nazw JavaScript (zapobieganie zanieczyszczeniu przestrzeni nazw) i nie blokując jednocześnie żadnych istniejących obiektów na ścieżce przestrzeni nazw .

Z powyższego wynika, że ​​było to moje rozwiązanie około roku 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;
};

To nie tworzy przestrzeni nazw, ale zapewnia funkcję tworzenia przestrzeni nazw.

Można to skondensować do zminimalizowanej jednowarstwowej:

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

Przykład zastosowania:

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

Lub, jako jedno stwierdzenie:

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

Albo jest następnie wykonywany jako:

com.example.namespace.test();

Jeśli nie potrzebujesz obsługi starszych przeglądarek, zaktualizowana wersja:

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

Byłbym ostrożny z eksponowaniem namespacesamej globalnej przestrzeni nazw. (Szkoda, że ​​podstawowy język tego nam nie zapewnia!) Zwykle używałbym tego sam w zamknięciu, na przykład:

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

W większej aplikacji należy to zdefiniować tylko raz na początku ładowania strony (w przypadku aplikacji internetowych opartych na kliencie). Dodatkowe pliki mogą następnie ponownie użyć funkcji przestrzeni nazw, jeśli są przechowywane (zawarte powyżej jako „opcjonalne”). W najgorszym przypadku, jeśli funkcja ta zostanie zadeklarowana kilka razy - to tylko kilka wierszy kodu, a mniej, jeśli zostanie zminimalizowana.

ziesemer
źródło
2

Podoba mi się rozwiązanie Jaco Pretoriusa, ale chciałem uczynić słowo kluczowe „to” nieco bardziej użytecznym, kierując je do obiektu moduł / przestrzeń nazw. Moja wersja patelni:

(function ($, undefined) {

    console.log(this);

}).call(window.myNamespace = window.myNamespace || {}, jQuery);
haxpanel
źródło
2

Mój ulubiony wzór stał się ostatnio:

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

Oczywiście zwrot może nastąpić na końcu, ale jeśli podążają za nim tylko deklaracje funkcji, znacznie łatwiej jest zobaczyć, o co chodzi w przestrzeni nazw i jakie API jest widoczne.

Wzorzec używania wyrażeń funkcyjnych w takich przypadkach powoduje, że nie można dowiedzieć się, jakie metody są narażone, bez konieczności przeglądania całego kodu.

Nomaed
źródło
Cześć, jak wywołujesz funkcje publiczne ze swojego fragmentu? Próbowałemnamespace.a();
olimart
@olivier tak, to jest pomysł. Chociaż teraz w ES6, zwykle używam skróconej składni literałów obiektowych ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed,
2

Myślę, że wszyscy używacie zbyt dużo kodu, aby rozwiązać tak prosty problem. Nie trzeba za to robić repozytorium. Oto funkcja jednej linii.

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

Spróbuj :

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

Yairopro
źródło
1

Jeśli używasz pliku Makefile, możesz to zrobić.

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

W każdym razie wolę użyć pliku Makefile po przejściu do około 1000 linii, ponieważ mogę skutecznie komentować duże obszary kodu, usuwając jedną linię z pliku makefile. Ułatwia to zabawę. Ponadto dzięki tej technice przestrzeń nazw pojawia się tylko raz w preludium, więc łatwo ją zmienić i nie trzeba powtarzać jej w kodzie biblioteki.

Skrypt powłoki do programowania na żywo w przeglądarce podczas korzystania z makefile:

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

Dodaj to jako polecenie make „go”, a możesz „make go”, aby aktualizować kompilację podczas pisania kodu.

Samuel Danielson
źródło
1

Dość kontynuacja odpowiedzi Ionuța G. Stana, ale pokazująca zalety nieuporządkowanego kodu za pomocą var ClassFirst = this.ClassFirst = function() {...}, który wykorzystuje zakresy JavaScript zamykania w celu zmniejszenia zaśmiecania przestrzeni nazw dla klas w tej samej przestrzeni nazw.

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

Wynik:

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
lama12345
źródło
1

Napisałem kolejną bibliotekę przestrzeni nazw, która działa trochę bardziej niż pakiety / jednostki w innych językach. Pozwala ci stworzyć pakiet kodu JavaScript i odniesienie do tego pakietu z innego kodu:

Plik hello.js

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

Plik Przykład.js

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

Na stronie musi znajdować się tylko drugi plik. Jego zależności (plik hello.js w tym przykładzie) zostaną automatycznie załadowane, a obiekty wyeksportowane z tych zależności zostaną wykorzystane do wypełnienia argumentów funkcji wywołania zwrotnego.

Powiązany projekt można znaleźć w pakiecie JS .

Stijn de Witt
źródło
1
@ peter-mortensen Czy te zmiany mojej odpowiedzi z '11 były naprawdę konieczne? Na pewno nie robisz wandalizmu, nie zrozum mnie źle, ale są bardzo powierzchowne. Wolałbym pozostać jedynym autorem takich postów, chyba że naprawdę dodasz coś dobrego.
Stijn de Witt
1

Możemy używać go niezależnie w ten sposób:

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

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

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

A.B.itemTwo = function () {
    //..
}
ganesh
źródło
0

Moim nawykiem jest używanie funkcji myName () jako pamięci właściwości, a następnie var myName jako posiadacza „metody” ...

Niezależnie od tego, czy jest to wystarczająco uzasadnione, czy nie, pobij mnie! Cały czas polegam na mojej logice PHP i wszystko po prostu działa. :RE

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

Możesz to również zrobić na odwrót, aby sprawdzić przed utworzeniem obiektu, co jest znacznie lepsze :

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

Odniesienie do tego: JavaScript: Tworzenie obiektu za pomocą Object.create ()

Straszny
źródło
0

W JavaScript nie ma predefiniowanych metod korzystania z przestrzeni nazw. W JavaScript musimy stworzyć własne metody definiowania przestrzeni nazw. Oto procedura, którą stosujemy w technologiach Oodles.

Zarejestruj przestrzeń nazw Poniżej znajduje się funkcja rejestracji przestrzeni nazw

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

Aby zarejestrować Przestrzeń nazw, wystarczy wywołać powyższą funkcję z argumentem jako przestrzeń nazw oddzieloną '.'(kropką). Na przykład Niech twoja nazwa aplikacji to oodles. Możesz utworzyć przestrzeń nazw, wykonując następującą metodę

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

Zasadniczo utworzy strukturę NameSpaces, jak poniżej w backend:

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

W powyższej funkcji masz zarejestrowaną przestrzeń nazw o nazwie "oodles.HomeUtilities"i "oodles.GlobalUtilities". Aby wywołać te przestrzenie nazw, tworzymy zmienną, tj. Var $OHUi var $OGU.

Te zmienne są niczym innym jak aliasem do inicjalizacji przestrzeni nazw. Teraz, ilekroć zadeklarujesz funkcję, która należy do HomeUtilitiesciebie, zadeklarujesz ją następująco:

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

Powyżej znajduje się inicjalizacja nazwy funkcji i jest umieszczana w przestrzeni nazw $OHU. i do wywołania tej funkcji w dowolnym miejscu w plikach skryptów. Wystarczy użyć następującego kodu.

$OHU.initialization();

Podobnie z innymi nazwami.

Mam nadzieję, że to pomoże.

Saurabh Tiwary
źródło
0

JavaScript domyślnie nie obsługuje przestrzeni nazw. Jeśli więc utworzysz dowolny element (funkcję, metodę, obiekt, zmienną), staje się on globalny i zanieczyszcza globalną przestrzeń nazw. Weźmy przykład definiowania dwóch funkcji bez przestrzeni nazw,

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

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

Zawsze wywołuje definicję drugiej funkcji. W takim przypadku przestrzeń nazw rozwiąże problem kolizji nazw.

vishu2124
źródło