¿Cómo declaro un espacio de nombres en JavaScript?


990

¿Cómo creo un espacio de nombres en JavaScript para que mis objetos y funciones no sean sobrescritos por otros objetos y funciones con el mismo nombre? He usado lo siguiente:

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

¿Hay alguna forma más elegante o sucinta de hacer esto?


20
Puedo ver a dónde va con la comprobación para ver si se toma el espacio de nombres, pero dado que el objeto no se creará si esto falla, creo que el mejor enfoque es alertar si se toma el espacio de nombres. Francamente, esto no debería suceder en la mayoría de las situaciones de JS y debería detectarse rápidamente en el desarrollo.
annakata

18
Tome un "espacio de nombres" de nivel superior (propiedad de ventana). Me pertenece. Los conflictos deben detectarse al principio de la prueba. No se moleste en agregar todas estas comprobaciones de "qué pasaría si". Es un problema fatal para los "espacios de nombres" duplicados y debe tratarse como tal . Puede seguir un enfoque como jQuery para permitir habitar un "espacio de nombres" personalizado; pero esto sigue siendo un problema de tiempo de diseño.

Cambie su respuesta aceptada a stackoverflow.com/questions/881515/… , que es una solución mucho más elegante y actualizada.
hlfcoding

@pst ¿qué pasa con lo que hace YUI? Creo que hacen exactamente esto para agregar gradualmente a su espacio de nombres. ¿Trucos como este son necesarios para el rendimiento en un entorno HTTP?
Simon_Weaver

ver también stackoverflow.com/questions/2102591/… para problemas de rendimiento
Tim Abell

Respuestas:


764

Me gusta esto:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
El punto importante es ser religioso acerca de expandirse no más allá de la variable raíz. Todo debe fluir de esto.
annakata

22
Esto no crea un cierre para su código; hace que sea tedioso llamar a sus otras funciones porque siempre tienen que verse así: yourNamespace.bar (); Hice un proyecto de código abierto SOLO para abordar este problema de diseño: github.com/mckoss/namespace .
mckoss

24
annakata: "El punto importante es ser religioso acerca de expandirse no más allá de la variable raíz". ¿Por qué es esto?
user406905

11
@alex: ¿por qué debería haber una estructura de objeto poco profunda?
Ryan

25
@ Ryan quise decir que todo debería estar debajo MyApp, por ejemplo, en MyApp.Views.Profile = {}lugar de MyApp.users = {}y MyViews.Profile = {}. No necesariamente que solo debe haber dos niveles de profundidad.
alex

1042

Uso el enfoque que se encuentra en el sitio Enterprise jQuery :

Aquí está su ejemplo que muestra cómo declarar propiedades y funciones privadas y públicas. Todo se hace como una función anónima autoejecutable.

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

Entonces, si desea acceder a uno de los miembros públicos, simplemente vaya skillet.fry()oskillet.ingredients .

Lo realmente genial es que ahora puede extender el espacio de nombres usando exactamente la misma sintaxis.

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

El tercer undefinedargumento

El tercer undefinedargumento es la fuente de la variable de valor undefined. No estoy seguro de si todavía es relevante hoy, pero al trabajar con navegadores más antiguos / estándares de JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variable de alcance global undefinedse puede escribir, por lo que cualquiera podría reescribir su valor. El tercer argumento (cuando no se pasa un valor) crea una variable denominada undefinedque se limita al espacio de nombres / función. Como no se pasó ningún valor cuando creó el espacio de nombre, el valor predeterminado es el valor undefined.


99
+1 para esta gran muestra. Para cualquier persona interesada, esta muestra fue parte de la excelente presentación de Elijah Manor en Mix 2011 (ignore el título) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis

11
Del artículo de Elijah, aquí están los pros y los contras de este enfoque, parafraseados. Pros: 1. Propiedades y métodos públicos y privados, 2. no utiliza OLN engorroso, 3. Protege indefinido 4. Asegura que $ se refiere a jQuery, 5. El espacio de nombres puede abarcar archivos, Contras: Más difícil de entender que OLN
Jared Beck

44
Esto se llama hoy IIFE ( Expresión de función invocada inmediatamente ). Gracias por tu respuesta +1!
Gustavo Gondim

20
@CpILL: no estoy seguro si aún es relevante, pero el tercer undefinedargumento es la fuente de la variable de valor undefined. Al trabajar con navegadores más antiguos / estándar de JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4), la variable de alcance global undefinedse puede escribir, por lo que cualquiera podría reescribir su valor. Agregar un tercer argumento adicional que no está pasando lo hace valioso undefined, por lo que estaba creando un espacio de nombres undefinedque no será reescrito por fuentes externas.
mrówa

44
@SapphireSun El beneficio window.skillet = window.skillet || {}es que permite que múltiples scripts se agreguen de manera segura al mismo espacio de nombres cuando no saben de antemano en qué orden ejecutarán. Esto puede ser útil si desea poder reordenar sus inclusiones de script de forma arbitraria sin romper su código, o si desea cargar scripts de forma asincrónica con el atributo async y, por lo tanto, no tiene garantía sobre el orden de ejecución. Ver stackoverflow.com/questions/6439579/…
Mark Amery

338

Otra forma de hacerlo, que considero que es un poco menos restrictiva que la forma literal del objeto, es esta:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

Lo anterior es muy similar al patrón del módulo y, le guste o no , le permite exponer todas sus funciones como público, evitando la estructura rígida de un objeto literal.


16
1. Hay una diferencia entre OLN y el patrón del módulo. 2. No me gusta / siempre / me gusta OLN ya que debes recordar no poner la última coma final y todos tus atributos deben inicializarse con un valor (como nulo o indefinido). Además, si necesita cierres para funciones miembro, necesitará pequeñas fábricas de funciones para cada uno de esos métodos. Otra cosa es que debe encerrar todas sus estructuras de control dentro de las funciones, mientras que la forma anterior no impone eso. Eso no quiere decir que no use OLN, es solo que a veces no me gusta.
Ionuț G. Stan

8
Me gusta este enfoque porque permite funciones privadas, variables y pseudoconstantes (es decir, var API_KEY = 12345;).
Lawrence Barsanti

12
Me gusta más que el contenedor de objetos separados por comas que ha votado más alto. No veo ninguna deficiencia en comparación, tampoco. ¿Me estoy perdiendo de algo?
Lucent

77
JS Newbie aquí ... ¿por qué no tengo que escribir? ns().publicFunction()Es decir ... ns.publicFunction()funciona.
John Kraft

14
@ John Kraft, es la causa de la newpalabra clave delante de la functionpalabra clave. Básicamente, lo que está haciendo es declarar una función anónima (y como función, también es un constructor), e inmediatamente la invoca como un constructor usando new. Como tal, el valor final que se almacena dentro nses una instancia (única) de ese constructor anónimo. Espero que tenga sentido.
Ionuț G. Stan

157

¿Hay alguna forma más elegante o sucinta de hacer esto?

Si. Por ejemplo:

var your_namespace = your_namespace || {};

entonces puedes tener

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

1
Esto me da un error en IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: your_namespace; Funciona mejor
mjallday el

99
debe ser var your_namespace = your_namespace = your_namespace || {} Funciona en todos los navegadores;)
Palo

+1 de mi parte! Thin one funciona como la respuesta de Jaco Pretorius al expandir una biblioteca a diferentes archivos o diferentes lugares dentro del mismo archivo. ¡Sólo brillante!
Centurian

2
@Palo ¿Puede explicar por qué debería ser así? var your_namespace = your_namespace = your_namespace || {}
Sriram

66
Tendría la posibilidad de extender el objeto your_namespace en diferentes archivos js. Al usar var your_namespace = {} no puede hacerlo, ya que el objeto será anulado por cada archivo
Alex Pacurar

93

Normalmente lo construyo en un cierre:

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

Mi estilo a lo largo de los años ha tenido un cambio sutil desde que escribí esto, y ahora me encuentro escribiendo el cierre así:

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

De esta manera, encuentro que la API pública y la implementación son más fáciles de entender. Piense en la declaración de devolución como una interfaz pública para la implementación.


3
¿No deberías comprobarlo MYNS.subns = MYNS.subns || {}?
Mirko

Un buen punto que debería ser el ejercicio para la intención de los desarrolladores. Debe considerar qué hacer cuando exista, reemplazarlo, error, usar la verificación existente o de versión y reemplazar condicionalmente. He tenido diferentes situaciones que requieren cada una de las variantes. En la mayoría de los casos, posiblemente tenga esto como un caso límite de bajo riesgo y el reemplazo puede ser beneficioso, considere un módulo falso que intentó secuestrar el NS.
Brett Ryan el

1
Hay una explicación de este enfoque en el libro "Speaking Javascript" en la página 412 si alguien lo tiene, bajo el título "Módulos rápidos y sucios".
Soferio

2
Consejo de optimización: mientras que var foo = functiony function fooson similares, son privados; Debido a la naturaleza de tipo dinámico de JavaScript, este último es un poco más rápido ya que omite algunas instrucciones en la mayoría de las tuberías de los intérpretes. Con var foo, el sistema de tipos debe ser invocado para averiguar qué tipo se está asignando a dicha var, mientras que con function foo, el sistema de tipos sabe automáticamente que es una función, por lo que se saltan un par de llamadas de función, lo que se traduce en menos invocaciones de instrucciones de CPU como jmp, pushq, popq, etc, que se traduce en una tubería de CPU más corta.
Braden Best

1
@brett oops. Tienes razón. Estaba pensando en un lenguaje de script diferente. Aunque todavía insisto, la function foosintaxis es más legible. Y todavía me gusta mi versión.
Braden Best

56

Debido a que puede escribir diferentes archivos de JavaScript y luego combinarlos o no combinarlos en una aplicación, cada uno debe poder recuperar o construir el objeto de espacio de nombres sin dañar el trabajo de otros archivos ...

Un archivo podría intentar usar el espacio de nombres namespace.namespace1:

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

namespace.namespace1.doSomeThing = function(){}

Otro archivo puede querer usar el espacio de nombres namespace.namespace2:

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

namespace.namespace2.doSomeThing = function(){}

Estos dos archivos pueden vivir juntos o separados sin colisionar.


1
He encontrado que este es un método muy útil para organizar el script del cliente en múltiples archivos en grandes aplicaciones donde la funcionalidad debe ser modular.
DVK


49

Así es como Stoyan Stefanov lo hace en su libro de Patrones de JavaScript que me pareció muy bueno (también muestra cómo hace comentarios que permiten la documentación de API generada automáticamente y cómo agregar un método al prototipo de un objeto personalizado):

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

Yo uso este enfoque:

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

El código externo puede ser:

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

¡Gran detalle! ¡Gracias! Me pregunto cuál es su opinión sobre Namespace.js. Nunca lo he usado, así que me pregunto si alguien con su conocimiento / habilidad / experiencia consideraría usarlo.
John

¡Me gusta! Por otro lado, obtengo una excepción en la primera línea de este código externo, que dice: 'myNameSpace.MyClass' [undefined] no es un constructor. tal vez depende de la implementación de JS? : /
yoosiba

@yossiba: Posiblemente. El código anterior es bastante estándar. En JS estándar, cualquier función se puede usar como un constructor, no hay nada que deba hacer para marcar una función específicamente para ser utilizada como un constructor. ¿Estás usando un sabor inusual como ActionScript o algo así?
AnthonyWJones

@Anthony es mejor usar var MYNAMESPACE = MYNAMESPACE || {}; simplemente usar var myNamespace = {} no es seguro y además es mejor declarar su espacio de nombres en mayúsculas
paul

99
@paul: "Mejor" puede ser bastante subjetivo. Odio leer el código que me GRITA, así que evito usar identificadores que usen todo en mayúsculas. Si bien ns = ns || {}puede parecer más defensivo, puede conducir a otros resultados inesperados.
AnthonyWJones

32

Este es un seguimiento del enlace de user106826 a Namespace.js. Parece que el proyecto se trasladó a GitHub . Ahora es smith / namespacedotjs .

He estado usando este simple ayudante de JavaScript para mi pequeño proyecto y hasta ahora parece ser lo suficientemente ligero pero versátil como para manejar el espacio de nombres y cargar módulos / clases. Sería genial si me permitiera importar un paquete en un espacio de nombres de mi elección, no solo en el espacio de nombres global ... suspiro, pero eso no viene al caso.

Le permite declarar el espacio de nombres y luego definir objetos / módulos en ese espacio de nombres:

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

Otra opción es declarar el espacio de nombres y su contenido a la vez:

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

Para obtener más ejemplos de uso, mire el archivo example.js en la fuente .


2
Siempre que recuerde, esto tiene algunas implicaciones de rendimiento, ya que cada vez que accede a my.awesome.package.WildClass está accediendo a la propiedad impresionante de my, la propiedad del paquete de my.awesome y la propiedad WildClass de my.awesome. paquete.
SamStephens

29

Muestra:

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

Opcionalmente, puede declarar una localvariable, samecomo selfy asignar local.onTimeoutsi desea que sea privada.


14

Puede declarar una función simple para proporcionar espacios de nombres.

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

Si necesita el ámbito privado:

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

de lo contrario, si nunca usará el ámbito privado:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();

12

El patrón Módulo se definió originalmente como una forma de proporcionar encapsulación tanto privada como pública para las clases de ingeniería de software convencional.

Cuando trabaje con el patrón Módulo, podemos encontrarnos útil para definir una plantilla simple que usamos para comenzar con él. Aquí hay uno que cubre el espaciado de nombres, las variables públicas y privadas.

En JavaScript, el patrón Módulo se usa para emular aún más el concepto de clases de tal manera que podamos incluir tanto métodos públicos / privados como variables dentro de un solo objeto, protegiendo así partes particulares del alcance global. Esto resulta en una reducción en la probabilidad de que nuestros nombres de funciones entren en conflicto con otras funciones definidas en scripts adicionales en la página.

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

    }
  };

})();

Ventajas

¿Por qué el patrón Módulo es una buena opción? Para empezar, es mucho más limpio para los desarrolladores que provienen de un entorno orientado a objetos que la idea de una verdadera encapsulación, al menos desde una perspectiva de JavaScript.

En segundo lugar, admite datos privados, por lo que, en el patrón Módulo, las partes públicas de nuestro código pueden tocar las partes privadas, sin embargo, el mundo exterior no puede tocar las partes privadas de la clase.

Desventajas

Las desventajas del patrón del Módulo son que a medida que accedemos a los miembros públicos y privados de manera diferente, cuando deseamos cambiar la visibilidad, en realidad tenemos que hacer cambios en cada lugar donde se usó el miembro.

Tampoco podemos acceder a miembros privados en métodos que se agregan al objeto en un momento posterior . Dicho esto, en muchos casos el patrón del Módulo sigue siendo bastante útil y, cuando se usa correctamente, ciertamente tiene el potencial de mejorar la estructura de nuestra aplicación.

El patrón del módulo revelador

Ahora que estamos un poco más familiarizados con el patrón del módulo, echemos un vistazo a una versión ligeramente mejorada: el patrón del módulo revelador de Christian Heilmann.

El patrón del Módulo Revelador surgió cuando Heilmann estaba frustrado con el hecho de que tenía que repetir el nombre del objeto principal cuando queríamos llamar a un método público desde otro o acceder a variables públicas. También le disgustaba el requisito del patrón del Módulo para tener que cambiar objetar la notación literal de las cosas que deseaba hacer públicas.

El resultado de sus esfuerzos fue un patrón actualizado en el que simplemente definiríamos todas nuestras funciones y variables en el ámbito privado y devolveríamos un objeto anónimo con punteros a la funcionalidad privada que deseábamos revelar como pública.

Un ejemplo de cómo usar el patrón del Módulo Revelador se puede encontrar a continuación

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

Ventajas

Este patrón permite que la sintaxis de nuestros scripts sea más consistente. También deja más claro al final del módulo cuáles de nuestras funciones y variables se pueden acceder públicamente, lo que facilita la legibilidad.

Desventajas

Una desventaja de este patrón es que si una función privada se refiere a una función pública, esa función pública no puede anularse si es necesario un parche. Esto se debe a que la función privada seguirá haciendo referencia a la implementación privada y el patrón no se aplica a los miembros públicos, solo a las funciones.

Los miembros de objetos públicos que se refieren a variables privadas también están sujetos a las notas de la regla sin parche anteriores.


9

Creé un espacio de nombres inspirado en los módulos de Erlang. Es un enfoque muy funcional, pero así es como escribo mi código JavaScript en estos días.

Le da a un cierre un espacio de nombres global y expone un conjunto de funciones definidas dentro de ese cierre.

(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

Después de portar varias de mis bibliotecas a diferentes proyectos, y tener que cambiar constantemente el espacio de nombres de nivel superior (con nombre estático), he cambiado a usar esta pequeña función auxiliar (de código abierto) para definir espacios de nombres.

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

La descripción de los beneficios se encuentra en mi blog . Puedes obtener el código fuente aquí .

Uno de los beneficios que realmente me gusta es el aislamiento entre módulos con respecto al orden de carga. Puede consultar un módulo externo ANTES de que se cargue. Y la referencia de objeto que obtenga se completará cuando el código esté disponible.


1
He creado una versión mejorada (2.0) de la biblioteca de espacio de nombres: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss

todos sus enlaces parecen muertos
snoob dogg

8

Yo uso la siguiente sintaxis para el espacio de nombres.

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

Llegué 7 años tarde a la fiesta, pero trabajé bastante por esto hace 8 años:

Es importante poder crear de manera fácil y eficiente múltiples espacios de nombres anidados para mantener una aplicación web compleja organizada y manejable, respetando el espacio de nombres global de JavaScript (evitando la contaminación del espacio de nombres) y sin bloquear ningún objeto existente en la ruta del espacio de nombres mientras lo hace. .

De lo anterior, esta fue mi solución circa-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;
};

Esto no crea un espacio de nombres, sino que proporciona una función para crear espacios de nombres.

Esto se puede condensar en una sola línea minificada:

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

Ejemplo de uso:

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

O, como una declaración:

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

Luego se ejecuta como:

com.example.namespace.test();

Si no necesita soporte para navegadores heredados, una versión actualizada:

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

Ahora, desconfiaría de exponerme namespaceal propio espacio de nombres global. (¡Lástima que el idioma base no nos proporcione esto!) Por lo general, lo usaría yo mismo en un cierre, como:

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

En una aplicación más grande, esto solo debe definirse una vez al comienzo de la carga de una página (para aplicaciones web basadas en el cliente). Los archivos adicionales pueden reutilizar la función de espacio de nombres si se mantienen (incluidos como "opcionales" en lo anterior). En el peor de los casos, si esta función se vuelve a declarar varias veces, solo son unas pocas líneas de código, y menos si se minimiza.


3

Creo que todos ustedes usan demasiado código para un problema tan simple. No es necesario hacer un repositorio para eso. Aquí hay una función de una sola línea.

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

Intentalo :

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

Me gusta la solución de Jaco Pretorius, pero quería hacer que la palabra clave "this" sea un poco más útil al señalarla al objeto de módulo / espacio de nombres. Mi versión de sartén:

(function ($, undefined) {

    console.log(this);

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

2

Mi patrón favorito se ha convertido últimamente en esto:

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

Por supuesto, el retorno puede ser al final, pero si solo lo siguen las declaraciones de funciones, es mucho más fácil ver de qué se trata el espacio de nombres y qué API está expuesta.

El patrón de usar expresiones de función en tales casos resulta en no poder saber qué métodos están expuestos sin pasar por todo el código.


Hola, ¿cómo llamas a funciones públicas desde tu fragmento? Lo he intentadonamespace.a();
olimart

@olivier sí, esa es la idea. Aunque ahora con ES6, generalmente uso la sintaxis abreviada de los literales de objetos ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed

1

Si usa un Makefile puede hacer esto.

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

Prefiero usar un Makefile de todos modos una vez que llegue a aproximadamente 1000 líneas porque puedo comentar efectivamente grandes extensiones de código eliminando una sola línea en el makefile. Hace que sea fácil jugar con cosas. Además, con esta técnica, el espacio de nombres solo aparece una vez en el preludio, por lo que es fácil de cambiar y no tiene que seguir repitiéndolo dentro del código de la biblioteca.

Un script de shell para el desarrollo en vivo en el navegador cuando se utiliza un archivo MAKE:

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

Agregue esto como una tarea de creación 'go' y puede 'hacer que funcione' para mantener su compilación actualizada mientras codifica.


1

Todo un seguimiento de la respuesta de Ionuț G. Stan, pero mostrando los beneficios del código ordenado mediante el uso var ClassFirst = this.ClassFirst = function() {...}, que aprovecha el alcance de cierre de JavaScript para un menor desorden de espacio de nombres para las clases en el mismo espacio de nombres.

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

Salida:

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

He escrito otra biblioteca de espacios de nombres que funciona un poco más como lo hacen los paquetes / unidades en otros idiomas. Le permite crear un paquete de código JavaScript y la referencia a ese paquete desde otro código:

Archivo hello.js

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

Archivo Ejemplo.js

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

Solo el segundo archivo debe incluirse en la página. Sus dependencias (archivo hello.js en este ejemplo) se cargarán automáticamente y los objetos exportados desde esas dependencias se usarán para completar los argumentos de la función de devolución de llamada.

Puede encontrar el proyecto relacionado en Paquetes JS .


1
@ peter-mortensen ¿Fueron realmente necesarias estas ediciones de mi respuesta del '11? Definitivamente no es vandalismo lo que estás haciendo, no me malinterpretes, pero son muy superficiales. Preferiría seguir siendo el único autor de publicaciones como estas a menos que realmente agregue algo bueno.
Stijn de Witt

1

Podemos usarlo independientemente de esta manera:

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

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

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

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

0

Mi costumbre es usar la función myName () como almacenamiento de propiedades, y luego var myName como titular del "método" ...

Si esto es lo suficientemente legítimo o no, ¡golpéame! Confío en mi lógica PHP todo el tiempo, y las cosas simplemente funcionan. :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();

También puede hacerlo de una manera 'viceversa' para verificar antes de la creación de objetos, lo cual es mucho mejor :

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

Referencia a esto: JavaScript: creación de objetos con Object.create ()


0

En JavaScript no hay métodos predefinidos para usar espacios de nombres. En JavaScript tenemos que crear nuestros propios métodos para definir NameSpaces. Aquí hay un procedimiento que seguimos en las tecnologías Oodles.

Registrar un espacio de nombres A continuación se muestra la función para registrar un espacio de nombres

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

Para registrar un espacio de nombres simplemente llame a la función anterior con el argumento como espacio de nombres separado por '.'(punto). Por ejemplo, deje que el nombre de su aplicación sea montones. Puede crear un espacio de nombres siguiendo el método

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

Básicamente creará su estructura NameSpaces como se muestra a continuación en el backend:

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

En la función anterior tiene registrado un espacio de nombres llamado "oodles.HomeUtilities"y "oodles.GlobalUtilities". Para llamar a estos espacios de nombres hacemos una variable, es decir, var $OHUy var $OGU.

Estas variables no son más que un alias para inicializar el espacio de nombres. Ahora, cada vez que declare una función que le pertenezca HomeUtilities, la declarará de la siguiente manera:

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

Arriba está la inicialización del nombre de la función y se coloca en un espacio de nombres $OHU. y llamar a esta función en cualquier parte de los archivos de script. Solo usa el siguiente código.

$OHU.initialization();

Del mismo modo, con el otro NameSpaces.

Espero eso ayude.


0

JavaScript no admite el espacio de nombres de forma predeterminada. Entonces, si crea cualquier elemento (función, método, objeto, variable), entonces se vuelve global y contamina el espacio de nombres global. Tomemos un ejemplo de definición de dos funciones sin ningún espacio de nombres,

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

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

Siempre llama a la segunda definición de función. En este caso, el espacio de nombres resolverá el problema de colisión de nombres.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.