Como declaro um espaço para nome em JavaScript?


990

Como crio um espaço para nome em JavaScript para que meus objetos e funções não sejam substituídos por outros objetos e funções com o mesmo nome? Eu usei o seguinte:

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

Existe uma maneira mais elegante ou sucinta de fazer isso?


20
Posso ver para onde você está indo com a verificação para ver se o espaço para nome é usado, mas como o objeto não será criado se isso falhar, acho que a melhor abordagem é alertar se o espaço para nome é usado. Francamente, isso não deve acontecer na maioria das situações de JS e deve ser detectado rapidamente no desenvolvimento.
Annakata 19/05/09

18
Pegue um "namespace" de nível superior (propriedade da janela). Possui. Os conflitos devem ser detectados logo no início dos testes. Não se preocupe em adicionar todas essas verificações "what if". É um problema fatal para "espaços de nome" duplicados e deve ser tratado como tal . Você pode seguir uma abordagem como jQuery para permitir habitar um "espaço para nome" personalizado; mas esse ainda é um problema de tempo de design.

Altere sua resposta aceita para stackoverflow.com/questions/881515/… , que é uma solução muito mais elegante e atualizada.
Hlfcoding 30/09/10

@pst e o que a YUI faz? Eu acredito que eles fazem exatamente isso para adicionar gradualmente ao seu espaço para nome. truques como esse são necessários para o desempenho em um ambiente HTTP, com certeza?
Simon_Weaver

ver também stackoverflow.com/questions/2102591/... para problemas de desempenho
Tim Abell

Respostas:


764

Eu gosto disso:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

62
O ponto importante é ser religioso sobre a expansão não mais do que a única variável raiz. Tudo deve fluir disso.
Annakata 19/05/09

22
Isso não cria um fechamento para o seu código - torna chato chamar suas outras funções porque elas sempre têm a aparência de: yourNamespace.bar (); Eu criei um projeto de código aberto APENAS para resolver esse problema de design: github.com/mckoss/namespace .
Mckoss #

24
annakata: "O ponto importante é ser religioso sobre expandir não mais do que a única variável raiz". - Por que isso?
user406905

11
@alex - por que deveria haver uma estrutura de objeto rasa?
21712 Ryan

25
@ Ryan, quis dizer que tudo deve estar abaixo MyApp, por exemplo, em MyApp.Views.Profile = {}vez de MyApp.users = {}e MyViews.Profile = {}. Não necessariamente que deva haver apenas dois níveis de profundidade.
alex

1042

Eu uso a abordagem encontrada no site Enterprise jQuery :

Aqui está o exemplo deles, mostrando como declarar propriedades e funções privadas e públicas. Tudo é feito como uma função anônima de execução automática.

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

Portanto, se você deseja acessar um dos membros públicos, basta ir skillet.fry()ou skillet.ingredients.

O mais legal é que agora você pode estender o espaço para nome usando exatamente a mesma sintaxe.

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

O terceiro undefinedargumento

O terceiro undefinedargumento é a fonte da variável de valor undefined. Não tenho certeza se ainda é relevante hoje, mas, ao trabalhar com navegadores mais antigos / padrões JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4), a variável de escopo global undefinedé gravável, para que qualquer pessoa possa reescrever seu valor. O terceiro argumento (quando não é passado um valor) cria uma variável chamada undefinedcujo escopo é o espaço de nomes / função. Como nenhum valor foi passado quando você criou o espaço para nome, o padrão é o valor undefined.


9
+1 para esta excelente amostra. Para quem estiver interessado, esta amostra foi parte da excelente apresentação de Elias Manor at Mix 2011 (ignorar o título) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Darren Lewis

11
No artigo de Elijah, aqui estão os prós e contras dessa abordagem, parafraseada. Prós: 1. Propriedades e métodos públicos e privados, 2. não usa OLN pesado, 3. Protege indefinido 4. Garante que $ se refira ao jQuery, 5. O espaço para nome pode abranger arquivos, Contras: Mais difícil de entender que o OLN
Jared Beck

4
Isso é chamado hoje de IIFE ( expressão de função chamada imediatamente ). Obrigado pela sua resposta +1!
Gustavo Gondim

20
@ CPP: não tenho certeza se ainda é relevante, mas o terceiro undefinedargumento é a fonte da variável de valor undefined. Ao trabalhar com navegadores mais antigos / padrão javascript (ecmascript 5, javascript <1.8.5 ~ firefox 4), a variável de escopo global undefinedé gravável, para que qualquer pessoa possa reescrever seu valor. Adicionar um terceiro argumento adicional que você não está passando vale a pena undefined, então você estava criando um espaço para nome undefinedque não seria reescrito por fontes externas.
Mrówa

4
@SapphireSun A vantagem window.skillet = window.skillet || {}disso é que ele permite que vários scripts sejam adicionados com segurança ao mesmo espaço para nome quando eles não souberem antecipadamente em que ordem serão executados. Isso pode ser útil se você deseja reorganizar suas inclusões de script arbitrariamente sem quebrar seu código ou se deseja carregar scripts de forma assíncrona com o atributo async e, portanto, não tem garantia sobre a ordem de execução. Veja stackoverflow.com/questions/6439579/…
Mark Amery

338

Outra maneira de fazê-lo, que considero um pouco menos restritiva que a forma literal do objeto, é esta:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

A descrição acima é muito parecida com o padrão do módulo e, quer você goste ou não , permite expor todas as suas funções como públicas, evitando a estrutura rígida de um literal de objeto.


16
1. Há uma diferença entre o OLN e o padrão do módulo. 2. Eu não / sempre / gosto do OLN, pois você deve se lembrar de não colocar a última vírgula à direita e todos os seus atributos devem ser inicializados com um valor (como nulo ou indefinido). Além disso, se você precisar de fechamentos para funções-membro, precisará de pequenas fábricas de funções para cada um desses métodos. Outra coisa é que você deve incluir todas as suas estruturas de controle dentro das funções, enquanto a forma acima não impõe isso. Isso não quer dizer que eu não uso o OLN, é que às vezes eu não gosto.
Ionuț G. Stan

8
Eu gosto dessa abordagem porque permite funções privadas, variáveis ​​e pseudo-constantes (ou seja, var API_KEY = 12345;).
Lawrence Barsanti

12
Eu gosto disso melhor do que o contêiner de objetos separados por vírgula que é votado mais alto. Também não vejo deficiências em comparação. Estou esquecendo de algo?
Lucent

7
JS Novato aqui ... por que não preciso digitar ns().publicFunction(), ou seja, ns.publicFunction()funciona.
John Kraft

14
@ John Kraft, é por causa da newpalavra - chave na frente da functionpalavra - chave. Basicamente, o que está fazendo é declarar uma função anônima (e, como uma função, também é um construtor) e, em seguida, chama-a imediatamente como um construtor usando new. Como tal, o valor final que é armazenado dentro nsé uma instância (exclusiva) desse construtor anônimo. Espero que faça sentido.
Ionuț G. Stan

157

Existe uma maneira mais elegante ou sucinta de fazer isso?

Sim. Por exemplo:

var your_namespace = your_namespace || {};

então você pode ter

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

1
isso me dá um erro no IE7. var your_namespace = (tipo de your_namespace == "undefined" ||! your_namespace)? {}: your_namespace; funciona melhor.
Mjallday #

9
deve ser var your_namespace = your_namespace = your_namespace || {} Funciona em todos os navegadores;)
Palo

+1 de mim! O Thin funciona como resposta de Jaco Pretorius, expandindo uma biblioteca para arquivos diferentes ou lugares diferentes no mesmo arquivo. Simplesmente brilhante!
centurian

2
@ Palo Você pode explicar por que deveria ser assim? var your_namespace = your_namespace = your_namespace || {}
Sriram

6
você teria a possibilidade de estender o objeto your_namespace em diferentes arquivos js. Ao usar var your_namespace = {} você não pode fazer isso, pois o objeto será substituído por cada arquivo
Alex Pacurar

93

Normalmente, eu o construo em um fechamento:

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

Meu estilo ao longo dos anos teve uma mudança sutil desde que escrevi isso, e agora me pego escrevendo o fechamento assim:

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

Dessa maneira, acho a API pública e a implementação mais fáceis de entender. Pense na declaração de retorno como uma interface pública para a implementação.


3
Você não deve procurar MYNS.subns = MYNS.subns || {}?
Mirko

Um bom ponto que deve ser o exercício para a intenção dos desenvolvedores. Você precisa considerar o que fazer quando existir, substituí-lo, erro, usar a verificação existente ou de versão e substituir condicionalmente. Eu tive situações diferentes que chamam para cada variantes. Na maioria dos casos, você pode ter isso como um caso de baixo risco e a substituição pode ser benéfica, considere um módulo invasor que tentou sequestrar o NS.
Brett Ryan

1
Há uma explicação dessa abordagem no livro "Falando Javascript" na página 412, se alguém a tiver, sob o título "Módulos rápidos e sujos".
Soferio 30/01

2
Dica de otimização: enquanto var foo = functione function foosão semelhantes, sendo privadas; devido à natureza de tipo dinâmico do JavaScript, o último é um pouco mais rápido, pois ignora algumas instruções nos pipelines da maioria dos intérpretes. Com var foo, o sistema de tipos precisa ser chamado para descobrir qual tipo está sendo atribuído ao referido var, enquanto, com function foo, o sistema de tipos sabe automaticamente que é uma função, então algumas chamadas de função são ignoradas, o que se traduz em menos invocações de instruções da CPU, como jmp, pushq, popq, etc, que se traduz em um oleoduto CPU mais curto.
Braden Best

1
@brett oops. Você está certo. Eu estava pensando em uma linguagem de script diferente. Embora eu ainda insista, a function foosintaxe é mais legível. E eu ainda gosto da minha versão.
Braden Best

56

Como você pode gravar arquivos diferentes de JavaScript e, posteriormente, combiná-los ou não em um aplicativo, cada um precisa poder recuperar ou construir o objeto de espaço para nome sem danificar o trabalho de outros arquivos ...

Um arquivo pode pretender usar o espaço para nome namespace.namespace1:

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

namespace.namespace1.doSomeThing = function(){}

Outro arquivo pode querer usar o espaço para nome namespace.namespace2:

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

namespace.namespace2.doSomeThing = function(){}

Esses dois arquivos podem viver juntos ou separados sem colidir.


1
Descobri que este é um método muito útil para organizar o script do cliente em vários arquivos em aplicativos grandes, onde a funcionalidade precisa ser modular.
DVK

Pergunta perguntando especificamente para múltiplos arquivos: stackoverflow.com/questions/5150124/...
Ciro Santilli郝海东冠状病六四事件法轮功

49

Veja como Stoyan Stefanov faz isso em seu livro JavaScript Patterns , que eu achei muito bom (também mostra como ele faz comentários que permitem a documentação da API gerada automaticamente e como adicionar um método ao protótipo de um 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

Eu uso essa abordagem:

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

O código externo pode ser:

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

Grande detalhe! Obrigado! Basta saber o que você acha do Namespace.js. Eu nunca o usei, então estou me perguntando se alguém com seu conhecimento / habilidade / experiência consideraria usá-lo.
John

Eu gosto disso! Por outro lado, recebo uma exceção na primeira linha desse código externo, dizendo: 'myNameSpace.MyClass' [indefinido] não é construtor. talvez isso depende da implementação do JS? : /
yoosiba 16/02

@yossiba: Possivelmente. O código acima é bastante padrão. No JS padrão, qualquer função pode ser usada como construtor, não há nada que você precise fazer para marcar uma função como especificamente para ser usada como construtor. Você está usando um sabor incomum como o ActionScript ou algo assim?
AnthonyWJones

@ Anthony, é melhor usar var MYNAMESPACE = MYNAMESPACE || {}; apenas usando var myNamespace = {} não é seguro e, além disso o seu melhor para declarar seu namespace em letras maiúsculas
paul

9
@ Paul: "Melhor" pode ser bastante subjetivo. Eu odeio ler código que CHEGA para mim, então evito usar identificadores que usam todas as letras maiúsculas. Embora ns = ns || {}possa parecer mais defensivo, pode levar a outros resultados inesperados.
AnthonyWJones

32

Este é um acompanhamento do link do user106826 para Namespace.js. Parece que o projeto foi transferido para o GitHub . Agora é smith / namespacedotjs .

Eu tenho usado esse simples auxiliar de JavaScript no meu pequeno projeto e, até agora, parece ser leve e versátil o suficiente para lidar com namespacing e carregar módulos / classes. Seria ótimo se me permitisse importar um pacote para um espaço para nome de minha escolha, não apenas para o espaço para nome global ... suspiro, mas isso está além do ponto.

Ele permite que você declare o espaço para nome e defina objetos / módulos nesse espaço para nome:

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

Outra opção é declarar o espaço para nome e seu conteúdo de uma vez:

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

Para obter mais exemplos de uso, consulte o arquivo example.js na fonte .


2
Desde que você se lembre de que isso tem implicações no desempenho, sempre que acessar my.awesome.package.WildClass, você estará acessando a propriedade awesome my, a propriedade package de my.awesome e a propriedade WildClass de my.awesome. pacote.
SamStephens 19/09/11

29

Amostra:

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, você pode declarar uma localvariável, samecurtir selfe atribuir, local.onTimeoutse desejar que ela seja privada.


14

Você pode declarar uma função simples para fornecer espaços para nome.

function namespace(namespace) {
    var object = this, tokens = namespace.split("."), token;

    while (tokens.length > 0) {
        token = tokens.shift();

        if (typeof object[token] === "undefined") {
            object[token] = {};
        }

        object = object[token];
    }

    return object;
}

// Usage example
namespace("foo.bar").baz = "I'm a value!";

13

Se você precisar do escopo 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();

caso contrário, se você nunca usará o escopo privado:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();

12

O padrão do módulo foi originalmente definido como uma maneira de fornecer encapsulamento público e privado para as classes na engenharia de software convencional.

Ao trabalhar com o padrão Módulo, podemos achar útil definir um modelo simples que usamos para começar a usá-lo. Aqui está um que abrange variáveis ​​de espaçamento entre nomes, públicas e privadas.

Em JavaScript, o padrão Module é usado para emular ainda mais o conceito de classes de forma que possamos incluir métodos e variáveis ​​públicos / privados em um único objeto, protegendo partes específicas do escopo global. O que isso resulta é uma redução na probabilidade de os nomes de nossas funções entrarem em conflito com outras funções definidas em scripts adicionais na 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 );

    }
  };

})();

Vantagens

por que o padrão do módulo é uma boa escolha? Para iniciantes, é muito mais limpo para os desenvolvedores que têm um background orientado a objetos do que a idéia de encapsulamento verdadeiro, pelo menos da perspectiva do JavaScript.

Em segundo lugar, ele suporta dados privados - portanto, no padrão Módulo, partes públicas do nosso código são capazes de tocar as partes privadas; no entanto, o mundo exterior não consegue tocar nas partes privadas da classe.

Desvantagens

As desvantagens do padrão do módulo são que, como acessamos os membros públicos e privados de maneira diferente, quando desejamos alterar a visibilidade, na verdade temos que fazer alterações em cada local em que o membro foi usado.

Também não podemos acessar membros privados em métodos que são adicionados ao objeto posteriormente . Dito isto, em muitos casos, o padrão do módulo ainda é bastante útil e, quando usado corretamente, certamente tem o potencial de melhorar a estrutura do nosso aplicativo.

O padrão revelador do módulo

Agora que estamos um pouco mais familiarizados com o padrão do módulo, vamos dar uma olhada em uma versão um pouco melhorada - o padrão do módulo revelador de Christian Heilmann.

O padrão Revealing Module surgiu quando Heilmann ficou frustrado com o fato de ter que repetir o nome do objeto principal quando desejávamos chamar um método público de outro ou acessar variáveis ​​públicas. objetar notação literal pelas coisas que ele desejava tornar públicas.

O resultado de seus esforços foi um padrão atualizado, onde simplesmente definimos todas as nossas funções e variáveis ​​no escopo privado e retornamos um objeto anônimo com ponteiros para a funcionalidade privada que desejamos revelar como pública.

Um exemplo de como usar o padrão Revealing Module pode ser encontrado abaixo

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

Vantagens

Esse padrão permite que a sintaxe de nossos scripts seja mais consistente. Também fica mais claro no final do módulo quais de nossas funções e variáveis ​​podem ser acessadas publicamente, o que facilita a legibilidade.

Desvantagens

Uma desvantagem desse padrão é que, se uma função privada se refere a uma função pública, essa função pública não pode ser substituída se um patch for necessário. Isso ocorre porque a função privada continuará se referindo à implementação privada e o padrão não se aplica aos membros públicos, apenas às funções.

Membros de objetos públicos que se referem a variáveis ​​privadas também estão sujeitos às notas de regra sem patch acima.


9

Eu criei um namespace inspirado nos módulos de Erlang. É uma abordagem muito funcional, mas é assim que escrevo meu código JavaScript atualmente.

Ele fornece ao fechamento um espaço para nome global e expõe um conjunto de funções definidas dentro desse fechamento.

(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

Depois de portar várias das minhas bibliotecas para projetos diferentes, e ter que mudar constantemente o espaço para nome de nível superior (nomeado estaticamente), passei a usar essa pequena função auxiliar (de código aberto) para definir espaços de nome.

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

A descrição dos benefícios está no meu blog . Você pode pegar o código fonte aqui .

Um dos benefícios de que realmente gosto é o isolamento entre os módulos em relação à ordem de carregamento. Você pode consultar um módulo externo ANTES de ser carregado. E a referência do objeto que você obtém será preenchida quando o código estiver disponível.


1
Criei uma versão aprimorada (2.0) da biblioteca de namespace: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss

todos os seus links parecem mortos
snoob dogg 23/07/19

8

Eu uso a seguinte sintaxe para o espaço para nome.

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

Estou 7 anos atrasado para a festa, mas trabalhei bastante nisso há 8 anos:

É importante poder criar com facilidade e eficiência vários espaços para nome aninhados para manter um aplicativo Web complexo organizado e gerenciável, respeitando o espaço para nome global JavaScript (evitando a poluição do espaço para nome) e sem prejudicar nenhum objeto existente no caminho do espaço para nome. .

Do exposto acima, esta foi minha solução por volta de 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;
};

Isso não está criando um espaço para nome, mas fornece uma função para criar espaços para nome.

Isso pode ser condensado em uma linha 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};

Exemplo de uso:

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

Ou, como uma declaração:

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

Ou é então executado como:

com.example.namespace.test();

Se você não precisar de suporte para navegadores herdados, uma versão atualizada:

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

Agora, eu ficaria desconfiado de expor namespaceao próprio espaço para nome global. (Pena que o idioma base não fornece isso para nós!) Portanto, eu normalmente usaria isso sozinho em um encerramento, 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);

Em um aplicativo maior, isso precisa ser definido apenas uma vez no início do carregamento da página (para aplicativos da Web baseados no cliente). Arquivos adicionais podem reutilizar a função de espaço para nome, se mantidos (incluído como "opcional" no acima). Na pior das hipóteses, se essa função for declarada novamente algumas vezes - são apenas algumas linhas de código e menos se minificadas.


3

Eu acho que todos vocês usam muito código para um problema tão simples. Não há necessidade de fazer um repo para isso. Aqui está uma função de linha única.

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

Tente :

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

Eu gosto da solução de Jaco Pretorius, mas queria tornar a palavra-chave "this" um pouco mais útil, apontando-a para o objeto module / namespace. Minha versão da frigideira:

(function ($, undefined) {

    console.log(this);

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

2

Meu padrão favorito tornou-se recentemente:

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

Obviamente, o retorno pode estar no final, mas se apenas as declarações de funções o seguirem, é muito mais fácil ver qual é o espaço para nome e qual API está exposta.

O padrão de usar expressões de função nesses casos resulta em não ser possível saber quais métodos estão expostos sem passar por todo o código.


Olá, como você chama funções públicas do seu snippet? Eu tenteinamespace.a();
olimart

@ olivier sim, essa é a ideia. Embora agora com ES6, que normalmente usar a sintaxe abreviada de literais objeto ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
Nomaed

1

Se estiver usando um Makefile, você pode fazer isso.

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

Prefiro usar um Makefile de qualquer maneira, quando chegar a cerca de 1000 linhas, porque posso comentar efetivamente grandes faixas de código removendo uma única linha no makefile. Isso facilita a mexer nas coisas. Além disso, com essa técnica, o espaço para nome aparece apenas uma vez no prelúdio, portanto é fácil alterar e você não precisa repeti-lo dentro do código da biblioteca.

Um script de shell para desenvolvimento ao vivo no navegador ao usar um makefile:

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

Adicione isso como uma tarefa make 'go' e você pode 'make go' para manter sua compilação atualizada enquanto codifica.


1

Bastante acompanhamento da resposta de Ionuț G. Stan, mas mostrando os benefícios do código não organizado usando var ClassFirst = this.ClassFirst = function() {...}, o que tira proveito do escopo de fechamento do JavaScript para menos confusão de espaço para nomes para classes no mesmo espaço para nome.

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

Resultado:

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

Eu escrevi outra biblioteca de namespaces que funciona um pouco mais como os pacotes / unidades em outros idiomas. Ele permite que você crie um pacote de código JavaScript e a referência a esse pacote a partir de outro código:

Arquivo hello.js

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

Arquivo Example.js

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

Somente o segundo arquivo precisa ser incluído na página. Suas dependências (arquivo hello.js neste exemplo) serão carregadas automaticamente e os objetos exportados dessas dependências serão usados ​​para preencher os argumentos da função de retorno de chamada.

Você pode encontrar o projeto relacionado em Pacotes JS .


1
@ peter-mortensen Essas edições da minha resposta de '11 foram realmente necessárias? Definitivamente não é vandalismo o que você está fazendo, não me entenda mal, mas eles são muito superficiais. Eu preferiria continuar sendo o único autor de posts como esses, a menos que você realmente adicione algo de bom.
Stijn de Witt

1

Podemos usá-lo independentemente desta maneira:

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

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

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

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

0

Meu hábito é usar a função myName () como armazenamento de propriedades e, em seguida, var myName como titular do "método" ...

Se isso é legítimo o suficiente ou não, me bata! Eu confio na minha lógica PHP o tempo todo, e as coisas simplesmente funcionam. : 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();

Você também pode fazer isso de uma maneira 'vice-versa' para verificar antes da criação do objeto o que é muito melhor :

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

Referência a isto: JavaScript: Criando Objeto com Object.create ()


0

No JavaScript, não há métodos predefinidos para usar espaços de nome. Em JavaScript, precisamos criar nossos próprios métodos para definir NameSpaces. Aqui está um procedimento que seguimos nas tecnologias Oodles.

Registrar um espaço para nome A seguir, é a função para registrar um espaço para nome

//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 um espaço para nome, basta chamar a função acima com o argumento como espaço para nome separado por '.'(ponto). Por exemplo, deixe o nome do seu aplicativo ser grande. Você pode criar um espaço para nome seguindo o método

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

Basicamente, ele criará sua estrutura NameSpaces como abaixo no back-end:

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

Na função acima, você registrou um espaço para nome chamado "oodles.HomeUtilities"e "oodles.GlobalUtilities". Para chamar esses namespaces, criamos uma variável ie var $OHUe var $OGU.

Essas variáveis ​​nada mais são do que um alias para inicializar o espaço para nome. Agora, sempre que você declarar uma função que pertence a HomeUtilitiesvocê, a declarará da seguinte maneira:

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

Acima está a inicialização do nome da função e é colocada em um espaço para nome $OHU. e chamar essa função em qualquer lugar dos arquivos de script. Basta usar o seguinte código.

$OHU.initialization();

Da mesma forma, com os outros NameSpaces.

Espero que ajude.


0

JavaScript não suporta namespace por padrão. Portanto, se você criar qualquer elemento (função, método, objeto, variável), ele se tornará global e poluirá o espaço para nome global. Vamos dar um exemplo de definição de duas funções sem nenhum espaço para nome,

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

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

Ele sempre chama a segunda definição de função. Nesse caso, o namespace resolverá o problema de colisão de nomes.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.