Как мне объявить пространство имен в JavaScript? - PullRequest
954 голосов
/ 19 мая 2009

Как мне создать пространство имен в JavaScript, чтобы мои объекты и функции не перезаписывались другими объектами и функциями с тем же именем? Я использовал следующее:

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

Есть ли более элегантный или лаконичный способ сделать это?

Ответы [ 27 ]

13 голосов
/ 28 апреля 2012

Вы можете объявить простую функцию для предоставления пространства имен.

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!";
11 голосов
/ 22 марта 2017

Если вам нужна частная область:

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

иначе, если вы никогда не будете использовать частную область:

var yourNamespace = {};

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

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

yourNamespace.publicMethod();
9 голосов
/ 04 июля 2010

Я создал пространство имен , которое вдохновлено модулями Эрланга. Это очень функциональный подход, но именно так я сейчас пишу свой код JavaScript.

Это дает закрытию глобальное пространство имен и предоставляет определенный набор функций внутри этого замыкания.

(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

})();
9 голосов
/ 31 января 2017

Шаблон Module был первоначально определен как способ обеспечить как частную, так и публичную инкапсуляцию для классов в традиционной разработке программного обеспечения.

При работе с шаблоном модуля может оказаться полезным определить простой шаблон, который мы используем для начала работы с ним. Вот тот, который охватывает пространство имен, публичные и приватные переменные.

В JavaScript шаблон Module используется для дальнейшей эмуляции концепции классов таким образом, что мы можем включать как открытые / закрытые методы, так и переменные в один объект, таким образом защищая определенные части от глобальной области видимости. Это приводит к уменьшению вероятности конфликта имен наших функций с другими функциями, определенными в дополнительных сценариях на странице.

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

    }
  };

})();

Преимущества

почему шаблон модуля является хорошим выбором? Начнем с того, что разработчикам, исходящим из объектно-ориентированного фона, гораздо чище, чем идее истинной инкапсуляции, по крайней мере с точки зрения JavaScript.

Во-вторых, он поддерживает личные данные - поэтому в шаблоне Module открытые части нашего кода могут касаться закрытых частей, однако внешний мир не может касаться закрытых частей класса.

Недостатки

Недостатки шаблона Module заключаются в том, что, поскольку мы по-разному обращаемся как к публичным, так и к закрытым членам, когда мы хотим изменить видимость, нам фактически необходимо вносить изменения в каждое место, где использовался член.

Мы также не можем получить доступ к закрытым членам в методах, которые будут добавлены к объекту позднее . Тем не менее, во многих случаях шаблон модуля по-прежнему весьма полезен, и при правильном использовании, безусловно, может улучшить структуру нашего приложения.

Шаблон модуля раскрытия

Теперь, когда мы немного более знакомы с шаблоном модуля, давайте взглянем на слегка улучшенную версию - шаблон модуля выявления Кристиана Хайльмана.

Паттерн «Модуль раскрытия» появился, когда Хейлманн был разочарован тем, что ему пришлось повторять имя основного объекта, когда мы хотели вызвать один открытый метод из другого или получить доступ к публичным переменным. Ему также не нравилось требование шаблона модуля необходимость переключаться на буквальное обозначение объекта для вещей, которые он хотел обнародовать.

Результатом его усилий стал обновленный шаблон, в котором мы просто определяем все наши функции и переменные в частной области и возвращаем анонимный объект с указателями на частную функциональность, которую мы хотели раскрыть как открытую.

Пример использования шаблона «Модуль раскрытия» можно найти ниже

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

Преимущества

Этот шаблон позволяет синтаксису наших сценариев быть более согласованным. Кроме того, в конце модуля становится более понятным, какие из наших функций и переменных могут быть доступны публично, что облегчает читабельность.

Недостатки

Недостаток этого шаблона заключается в том, что если частная функция ссылается на открытую функцию, эта открытая функция не может быть переопределена, если требуется исправление. Это связано с тем, что закрытая функция будет продолжать ссылаться на закрытую реализацию, а шаблон не применяется к открытым членам, только к функциям.

Члены открытого объекта, которые ссылаются на закрытые переменные, также подчиняются примечаниям к правилу no-patch выше.

8 голосов
/ 05 февраля 2015

Я использую следующий синтаксис для пространства имен.

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/

7 голосов
/ 27 марта 2010

После переноса нескольких моих библиотек в разные проекты и необходимости постоянно менять пространство имен верхнего уровня (со статическим именем), я переключился на использование этой небольшой вспомогательной функции (с открытым исходным кодом) для определения пространств имен.

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

Описание преимуществ в моем блоге . Вы можете получить исходный код здесь .

Одним из преимуществ, которые мне действительно нравятся, является изоляция между модулями в отношении порядка загрузки. Вы можете обратиться к внешнему модулю ДО того, как он будет загружен. И ссылка на объект, которую вы получите, будет заполнена, когда код станет доступен.

7 голосов
/ 28 ноября 2016

Я опоздал на вечеринку на 7 лет, но 8 лет назад проделал немало работы:

Важно иметь возможность легко и эффективно создавать несколько вложенных пространств имен, чтобы сохранить сложное веб-приложение организованным и управляемым, соблюдая при этом глобальное пространство имен JavaScript (предотвращая загрязнение пространства имен), а также не допуская слипания любых существующих объектов в пути пространства имен. при этом.

Исходя из вышеизложенного, это было мое решение около 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;
};

Это не создает пространство имен, но предоставляет функцию для создания пространств имен.

Это может быть сжато до миниатюрной однострочной:

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

Пример использования:

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

Или, как одно утверждение:

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

Либо тогда выполняется как:

com.example.namespace.test();

Если вам не нужна поддержка устаревших браузеров, обновленная версия:

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

Теперь я бы опасался выставлять namespace самому глобальному пространству имен. (Жаль, что базовый язык нам этого не дает!) Поэтому я обычно использую это сам в закрытии, например:

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

В более крупном приложении это необходимо определить только один раз в начале загрузки страницы (для клиентских веб-приложений). Дополнительные файлы могут затем повторно использовать функцию пространства имен, если они сохранены (включены как «необязательные» в приведенном выше). В худшем случае, если эту функцию повторно объявить несколько раз - это всего лишь несколько строк кода и меньше, если минимизироваться.

5 голосов
/ 19 мая 2009

Вы должны проверить Namespace.js out!

2 голосов
/ 20 января 2016

Моя любимая модель в последнее время стала такой:

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

Конечно, return может быть в конце, но если за ним следуют только объявления функций, гораздо проще увидеть, что такое пространство имен и какой API предоставляется.

Шаблон использования функциональных выражений в таких случаях приводит к невозможности узнать, какие методы предоставляются, не просматривая весь код.

2 голосов
/ 02 декабря 2014

Мне нравится решение Жако Преториуса, но я хотел сделать ключевое слово this более полезным, указав его на объект модуля / пространства имен. Моя версия сковороды:

(function ($, undefined) {

    console.log(this);

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