Как мне объявить пространство имен в JavaScript?


990

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

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

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


20
Я могу видеть, куда вы идете с проверкой, чтобы увидеть, занято ли пространство имен, но так как объект не будет создан, если это не удастся, я думаю, что лучший подход заключается в предупреждении, если пространство имен занято. Честно говоря, это не должно происходить в большинстве ситуаций JS и должно быстро обнаруживаться в процессе разработки.
Аннаката

18
Возьмите «пространство имен» верхнего уровня (свойство окна). Имей это. Конфликты должны быть обнаружены на ранних этапах тестирования. Не надо добавлять все эти проверки «что если». Это фатальная проблема для дублирующих «пространств имен» и должна рассматриваться как таковая . Вы можете использовать такой подход, как jQuery, чтобы разрешить использование собственного «пространства имен»; но это все еще проблема времени разработки.

Пожалуйста, измените ваш принятый ответ на stackoverflow.com/questions/881515/… , который является гораздо более элегантным и обновленным решением.
hlfcoding

@pst как насчет того, что делает YUI? Я считаю, что они делают именно это для постепенного добавления в свое пространство имен. уловки как это необходимы для производительности в среде HTTP, конечно?
Simon_Weaver

см. также stackoverflow.com/questions/2102591/… для проблем с производительностью
Тим Абелл

Ответы:


764

Мне это нравится:

var yourNamespace = {

    foo: function() {
    },

    bar: function() {
    }
};

...

yourNamespace.foo();

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

22
Это не создает замыкание для вашего кода - это утомительно вызывать ваши другие функции, потому что они всегда должны выглядеть так: yourNamespace.bar (); Я сделал проект с открытым исходным кодом просто для решения этой проблемы дизайна: github.com/mckoss/namespace .
Mckoss

24
annakata: «Важный момент - быть религиозным в расширении не более чем одной корневой переменной.» - Почему это так?
user406905

11
@alex - почему должна быть мелкая структура объекта?
Райан

25
@ Райан Я имел в виду, что все должно быть MyApp, например, MyApp.Views.Profile = {}а не MyApp.users = {}и MyViews.Profile = {}. Не обязательно, чтобы была только два уровня глубины.
Алекс

1042

Я использую подход, найденный на сайте Enterprise jQuery :

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

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

Так что если вы хотите получить доступ к одному из публичных участников, вы просто идете skillet.fry()или skillet.ingredients.

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

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

Третий undefinedаргумент

Третий undefinedаргумент является источником переменной значения undefined. Я не уверен, что это все еще актуально сегодня, но при работе со старыми стандартами браузеров / JavaScript (ecmascript 5, javascript <1.8.5 ~ firefox 4) переменная global-scope доступна undefinedдля записи, поэтому любой может переписать ее значение. Третий аргумент (если не передано значение) создает переменную с именем, undefinedкоторая ограничена пространством имен / функцией. Поскольку при создании пространства имен не было передано никакого значения, по умолчанию используется значение undefined.


9
+1 за этот отличный образец. Для всех, кто интересуется, этот образец был частью превосходной презентации Элайджи Мэнор на Mix 2011 (игнорируйте название) live.visitmix.com/MIX11/Sessions/Speaker/Elijah-Manor
Даррен Льюис,

11
Из статьи Элайджи перефразированы плюсы и минусы этого подхода. Плюсы: 1. Открытые и закрытые свойства и методы, 2. не использует громоздкий OLN, 3. защищает неопределенное 4. гарантирует, что $ относится к jQuery, 5. пространство имен может охватывать файлы, минусы: сложнее понять, чем OLN
Джаред Бек

4
Сегодня это называется IIFE ( выражение для немедленного вызова функции ). Спасибо за ваш ответ +1!
Густаво Гондим

20
@CpILL: не уверен, что все еще актуален, но третий undefinedаргумент является источником переменной значения undefined. При работе со старыми стандартами браузеров / javascript (ecmascript 5, javascript <1.8.5 ~ firefox 4) переменная global-scope доступна undefinedдля записи, поэтому любой может переписать ее значение. Добавление третьего, дополнительного аргумента, который вы не передаете, делает его ценным undefined, поэтому вы создавали область имен, undefinedкоторая не будет перезаписываться внешними источниками.
mrówa

4
@SapphireSun Преимущество window.skillet = window.skillet || {}заключается в том, что он позволяет нескольким сценариям безопасно добавлять одно и то же пространство имен, когда они заранее не знают, в каком порядке они будут выполняться. Это может быть полезно либо в том случае, если вы хотите иметь возможность произвольно переупорядочивать включения в сценарии, не нарушая код, либо если вы хотите загружать сценарии асинхронно с атрибутом async и, таким образом, не можете гарантировать порядок выполнения. См. Stackoverflow.com/questions/6439579/…
Марк Амери

338

Другой способ сделать это, который я считаю немного менее строгим, чем форма литерала объекта, заключается в следующем:

var ns = new function() {

    var internalFunction = function() {

    };

    this.publicFunction = function() {

    };
};

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


16
1. Есть разница между OLN и шаблоном модуля. 2. Мне не нравится / не всегда нравится OLN, так как вы должны помнить, чтобы не ставить последнюю запятую, и все ваши атрибуты должны быть инициализированы значением (например, null или undefined). Также, если вам нужны замыкания для функций-членов, вам понадобятся небольшие фабрики функций для каждого из этих методов. Другое дело, что вы должны заключить все свои управляющие структуры в функции, тогда как в приведенной выше форме это не предусмотрено. Это не значит, что я не использую OLN, просто иногда мне это не нравится.
Ionuț G. Stan

8
Мне нравится этот подход, потому что он допускает закрытые функции, переменные и псевдоконстанты (т. Е. Var API_KEY = 12345;).
Лоуренс Барсанти

12
Мне нравится это лучше, чем разделенный запятыми контейнер объектов, за который проголосовали выше. Я не вижу никаких недостатков в сравнении. Я что-то пропустил?
Lucent

7
JS Newbie здесь ... почему я не должен печатать ns().publicFunction(), то есть ... ns.publicFunction()работает.
Джон Крафт

14
@ Джон Крафт, это не ключевое newслово перед functionключевым словом. По сути, он объявляет анонимную функцию (и как функцию, а также конструктор), а затем немедленно вызывает ее как конструктор, используя new. Таким образом, последнее значение, которое хранится внутри, nsявляется (уникальным) экземпляром этого анонимного конструктора. Надеюсь, это имеет смысл.
Ionuț G. Stan

157

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

Да. Например:

var your_namespace = your_namespace || {};

тогда вы можете иметь

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

1
это дает мне ошибку в IE7. var your_namespace = (typeof your_namespace == "undefined" ||! your_namespace)? {}: ваше_имя пространства; работает лучше.
mjallday

9
это должно быть var your_namespace = your_namespace = your_namespace || {} Работает в любом браузере;)
Пало

+1 от меня! Тонкий работает как ответ Жако Преториуса, расширяя одну библиотеку до разных файлов или разных мест внутри одного и того же файла. Просто великолепно!
центуриан

2
@Palo Не могли бы вы объяснить, почему так должно быть? var your_namespace = your_namespace = your_namespace || {}
Шрирам

6
у вас будет возможность расширить объект your_namespace в разных файлах js. При использовании var your_namespace = {} вы не можете этого сделать, так как объект будет переопределен каждым файлом
Alex Pacurar

93

Я обычно строю это в замыкании:

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

Мой стиль за эти годы слегка изменился с тех пор, как я написал это, и теперь я нахожу, что пишу закрытие вот так:

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

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


3
Разве вы не должны проверять MYNS.subns = MYNS.subns || {}??
Мирко,

Хорошим моментом должно быть упражнение для намерения разработчиков. Вам необходимо продумать, что делать, когда он существует, заменить его, исправить ошибку, использовать существующую или проверку версии и условно заменить. У меня были разные ситуации, которые требуют каждого варианта. В большинстве случаев вы, возможно, имеете это как крайний случай низкого риска, и замена может быть полезной, рассмотрите мошеннический модуль, который попытался захватить NS.
Бретт Райан

1
Объяснение этого подхода можно найти в книге «Говорящий Javascript» на странице 412, если она есть, под заголовком «Быстрые и грязные модули».
Соферио

2
Совет по оптимизации: пока var foo = functionи function fooпохожи, будучи приватными; из-за динамически типизируемого характера JavaScript последний немного быстрее, поскольку пропускает несколько инструкций в конвейерах большинства интерпретаторов. С помощью var foo, система типов должна вызываться, чтобы выяснить, какой тип назначается для упомянутой переменной, в то время function fooкак система типов автоматически знает, что это функция, поэтому пара вызовов функции пропускается, что приводит к меньшему количеству вызовов инструкций процессора, таких как jmp, pushq, popq, и т.д., что приводит к более короткому трубопроводу центрального процессора.
Брэден Бест

1
@brett упс. Вы правы. Я думал о другом языке сценариев. Хотя я все еще настаиваю на том, что function fooсинтаксис более читабелен. И мне все еще нравится моя версия.
Брэден Бест

56

Поскольку вы можете писать разные файлы JavaScript, а затем объединять или не объединять их в приложении, каждый должен иметь возможность восстанавливать или создавать объект пространства имен без ущерба для работы других файлов ...

Один файл может использовать пространство имен namespace.namespace1:

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

namespace.namespace1.doSomeThing = function(){}

Другой файл может использовать пространство имен namespace.namespace2:

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

namespace.namespace2.doSomeThing = function(){}

Эти два файла могут жить вместе или отдельно, не сталкиваясь.


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

Вопрос, задаваемый специально для нескольких файлов: stackoverflow.com/questions/5150124/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

49

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

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

Я использую этот подход:

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

Внешний код может быть:

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

Отличная деталь! Спасибо! Просто интересно, что вы думаете о Namespace.js. Я никогда не использовал его сам, поэтому мне интересно, если бы кто-то с вашими знаниями / навыками / опытом подумал бы о его использовании.
Джон

Мне это нравится! С другой стороны, я получаю исключение в первой строке этого внешнего кода, говоря: «myNameSpace.MyClass» [undefined] не является конструктором. может это зависит от реализации JS? : /
yoosiba

@yossiba: возможно. Код выше является довольно стандартным материалом. В стандартном JS любая функция может быть использована в качестве конструктора, вам не нужно ничего делать, чтобы пометить функцию как предназначенную для использования в качестве конструктора. Используете ли вы необычный вкус, как ActionScript или что-то?
AnthonyWJones

@ Энтони, лучше использовать var MYNAMESPACE = MYNAMESPACE || {}; просто использовать var myNamespace = {} небезопасно и, кроме того, лучше объявить ваше пространство имен заглавными
буквами

9
@Paul: «Лучше» может быть довольно субъективным. Я ненавижу читать код, который мне кричит, поэтому я избегаю использования идентификаторов, которые используют все заглавные буквы. Хотя это ns = ns || {}может показаться более оборонительным, оно может привести к другим неожиданным результатам.
AnthonyWJones

32

Это продолжение ссылки user106826 на Namespace.js. Кажется, проект перешел на GitHub . Теперь это кузнец / namespacedotjs .

Я использовал этот простой помощник JavaScript для своего крошечного проекта, и пока он кажется легким, но достаточно универсальным для обработки пространства имен и загрузки модулей / классов. Было бы здорово, если бы это позволило мне импортировать пакет в пространство имен по своему выбору, не только в глобальное пространство имен ... вздох, но это не главное.

Это позволяет вам объявить пространство имен, а затем определить объекты / модули в этом пространстве имен:

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

Другой вариант - объявить пространство имен и его содержимое сразу:

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

Дополнительные примеры использования смотрите в файле example.js в источнике .


2
Пока вы помните, это влияет на производительность, поскольку каждый раз, когда вы обращаетесь к my.awesome.package.WildClass, вы получаете доступ к удивительному свойству my, свойству package my.awesome и свойству WildClass my.awesome. пакет.
SamStephens

29

Образец:

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

Вы можете опционально объявить localпеременную, sameнапример, like selfи assign, local.onTimeoutесли вы хотите, чтобы она была закрытой.


14

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

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

Если вам нужна частная сфера:

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

12

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

При работе с шаблоном 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 выше.


9

Я создал пространство имен которое вдохновлено модулями Эрланга. Это очень функциональный подход, но именно так я сейчас пишу свой код 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

})();

8

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

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

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

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


1
Я создал улучшенную версию (2.0) библиотеки пространства имен: code.google.com/p/pageforest/source/browse/appengine/static/src/…
mckoss

все ваши ссылки кажутся мертвыми
сноб дог

8

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

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

Я опоздал на вечеринку на 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);

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


3

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

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

Попробуй это :

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

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

(function ($, undefined) {

    console.log(this);

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

2

В последнее время мой любимый шаблон стал таким:

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 предоставляется.

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


Привет, как вы вызываете публичные функции из своего фрагмента? Я пробовалnamespace.a();
olimart

@olivier да, это идея. Хотя сейчас в ES6 я обычно использую сокращенный синтаксис литералов объектов ( ponyfoo.com/articles/es6-object-literal-features-in-depth )
января

1

Если вы используете Makefile, вы можете сделать это.

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

Я предпочитаю использовать Makefile в любом случае, когда доберусь до 1000 строк, потому что могу эффективно закомментировать большие участки кода, удалив одну строку в make-файле. Это позволяет легко возиться с вещами. Кроме того, с помощью этой техники пространство имен появляется в прелюдии только один раз, поэтому его легко изменить, и вам не нужно повторять его внутри кода библиотеки.

Скрипт оболочки для живой разработки в браузере при использовании make-файла:

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

Добавьте это как задачу make «go», и вы можете «сделать go», чтобы ваша сборка обновлялась по мере написания кода.


1

Довольно продолженный ответ Ionu G. Stan, но показывающий преимущества незагроможденного кода с помощью var ClassFirst = this.ClassFirst = function() {...}, который использует преимущества замкнутой области JavaScript для уменьшения загромождения пространства имен для классов в одном и том же пространстве имен.

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

Вывод:

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

Я написал другую библиотеку пространств имен, которая работает немного больше, чем пакеты / модули на других языках. Это позволяет вам создавать пакет кода JavaScript и ссылаться на этот пакет из другого кода:

Файл hello.js

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

Файл Example.js

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

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

Вы можете найти соответствующий проект в Пакеты JS .


1
@ peter-mortensen Были ли эти изменения моего ответа от '11 действительно необходимыми? Это определенно не вандализм, что вы делаете, не поймите меня неправильно, но они очень поверхностны. Я бы предпочел остаться единственным автором подобных сообщений, если вы действительно не добавите что-то хорошее.
Стейн де Витт

1

Мы можем использовать его независимо таким образом:

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

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

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

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

0

Моя привычка - использовать функцию myName () в качестве хранилища свойств, а затем var myName в качестве держателя метода.

Достаточно ли это законно или нет, бей меня! Я все время полагаюсь на свою логику PHP, и все просто работает. : 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();

Вы также можете сделать это «наоборот», чтобы проверить перед созданием объекта, что намного лучше :

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

Ссылка на это: JavaScript: Создание объекта с Object.create ()


0

В JavaScript нет предопределенных методов для использования пространств имен. В JavaScript мы должны создавать свои собственные методы для определения пространств имен. Вот процедура, которой мы следуем в технологиях Oodles.

Зарегистрируйте пространство имен. Ниже приведена функция регистрации пространства имен.

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

Чтобы зарегистрировать пространство имен, просто вызовите вышеуказанную функцию с аргументом в качестве пространства имен, разделенного '.'(точка). Например, пусть ваше приложение называется oodles. Вы можете сделать пространство имен следующим методом

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

В основном это создаст вашу структуру NameSpaces как ниже в backend:

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

В приведенной выше функции вы зарегистрировали пространство имен с именем "oodles.HomeUtilities"и "oodles.GlobalUtilities". Чтобы вызвать эти пространства имен, мы создаем переменную, т.е. var $OHUи var $OGU.

Эти переменные - не что иное, как псевдоним для инициализации пространства имен. Теперь, когда бы вы ни объявили принадлежащую HomeUtilitiesвам функцию, она будет объявлена ​​следующим образом:

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

Выше приведена инициализация имени функции, и она помещена в пространство имен $OHU. и вызывать эту функцию в любом месте файлов сценария. Просто используйте следующий код.

$OHU.initialization();

Аналогично с другими пространствами имен.

Надеюсь, поможет.


0

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

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

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

Он всегда вызывает определение второй функции. В этом случае пространство имен решит проблему конфликта имен.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.