Использование пространств имен для организации JavaScript-кода

Автор оригинала: Aaron Conran
  • Перевод
На текущий момент большинство web-приложений состоят из большого числа библиотек, виджетов и сниппетов из многих и многих источников. Следует помнить, что код других разработчиков может взаимодействовать с вашим кодом в случае, если происходит подключение обоих их на одной странице. А если вы оперируете глобальными переменными, то это и вовсе небезопасно.

Почему необходимо использовать пространства имен?


Возьмем, в качестве примера, форум Ext JS, который использует три совершенно различных набора скриптов, созданных разными производителями: Ext JS используется нами для расширения функционала, Google Analytics для отслеживания использования сайта плюс обычные скрипты форума vBulletin. На рисунке представлено каким образом весь этот код из различных источников включается в тело страницы. Вообразите себе количество возможных противоречий с еще большим ростом подключаемых файлов.
Включаемые файлы
Если взглянуть на закладку DOM отладчика Firebug, можно увидеть сотни переменных контекста window, созданных подключенными скриптами. В тоже самое время Ext JS объединяет все свои классы в едином пространстве имен Ext и далее организует их в виде отдельных пакетов.
Обзор DOM
Когда вы пишете собственный срипт, вам следует помещать все свои классы и синглтоны (singletone) в некие пространства имен чтобы предотвратить противоречия с кодом других разработчиков. Термин «пространство имен» определяется в Википедии (EN, RU) следующим образом: «…абстрактный контейнер предоставляющий контекст для содержащихся в нем элементов (имена, термины или слова) и позволяющий предотвратить возникновение неоднозначностей в случае существования элементов с одинаковыми именами…».
Пространства имен это важный инструмент разработчика, гарантирующий невозможность перезаписи одного кода другим. Ведь если иной разработчик определит переменную с таким же, как у вас именем, то существовавшее до этого определение будет перезаписано. Последний подключенный, в таком случае, фрагмент кода будет всегда одерживать верх.
Так как JavaScript является языком с функциональными областями видимости*, то создание функции или/и переменной не «обернутых» в функцию приводит к появлению их в глобальной области видимости (в контексте window). Чтобы предотвратить это разработчики помещают свои классы в объекты.
* — вероятно, автор имеет ввиду создание различных областей видимости, что достижимо в этом языке лишь с помощью функций (прим. пер.)

Пространства имен без Ext JS


Без Ext JS вы можете создать пространство имен следующим образом:
if (!App) App = {};
if (!App.form) App.form = {};
if (!App.data) App.data = {};

Ext.namespace


Объект Ext предоставляет метод Ext.namespace (или его шоткат Ext.ns), который проверяет создаваемое пространство имен на существование и создает его, если такового еще нет. Сначала следует определить первоначальный уровень пространства, а затем можно создавать различные подуровни-пакеты. Например, создадим пространство имен App и входящие в него пакеты form и data:
/* Ext.namespace создаст объекты с переданными именами, если они еще не существуют */
Ext.namespace('App', 'App.form', 'App.data');
 
/* Теперь можно определять новый класс, например SampleForm, внутри пакета App.form */
App.form.SampleForm = Ext.extend(Ext.form.FormPanel, {
    initComponent: function() {
        /* код настройки компонента */
       App.form.SampleForm.superclass.call(this);
   }
});
 
/* Определение MySingleton внутри пакета App.data */
App.data.MySingleton = function() {
    return {
        sampleStaticMethod: Ext.emptyFn
    };
}();

В завершение


Используемый в web-приложениях на стороне клиента код JavaScript становится все более сложным и сложным. Поэтому важность правильной организация совместной работы вашего кода и кода третьих сторон также вырастает. Использование пространств имен гарантирует защиту вашему коду от перезаписи другим, находящимся в глобальной области видимости, кодом.

От переводчика


Автор оригинала: Aaron Conran
Оригинал статьи: Use Namespaces to organize your JavaScript code
Данный хабратопик является кросс-постом из моего блога.

Внимание! После публикации мной перевода статьи Джозефа Сакалоса (Jozef Sakalos) «Создание сложного приложения в Ext», он связался со мной и попросил передать хабрапользователям следующее: «Я в меру своих знаний русского языка просмотрел комментарии по переводу и готов ответить на любые связанный со статьей вопросы в моем блоге в соответствующей теме. Единственная просьба — формулируйте вопросы на русском коротко, пожалуйста, чтобы мне было легче их понять.»

Комментарии 18

    0
    класс! пусть после этого только попробуют поныть на тему отсутствия IT-направленного материала на Хабре!
    автору спасибо и респект за перевод
      0
      Спасибо за перевод, действительно использование неймспейсов сводит к минимуму воздействия различных библиотек между собой.
        +1
        > Ext.namespace создаст объекты с переданными именами, если они еще не существуют

        > Определение MySingleton внутри пакета App.data

        А если объекты существуют, да еще и, например, внутри "пакета" App.data уже есть св-во MySingleton, тогда что?
        Нет, все-же в отсутствие необходимых инструментов для органиции подобных структур (namespaces и т.п.) хочешь или не хочешь, а "подглядывать" в чужие скрипты придется для того, чтобы создавать уникальные идентификаторы.
          0
          Там есть возможность вложеных нейм спейсов к примеру не знаю как на ExtJS но вот на YUI можно создать чтото вроде YAHOO.my.name.space и я уверен что уже свойство не потеряетса а вообще неймспейсы должны зависить в первую очередь от логики какая реализуетса
            0
            в java это реализовано идеально.
            ru.habrahabr.core.CoolClass
              0
              Я, видимо, напрасно перевел название статьи дословно. Наверное, следовало добавить "отсебятину" в виде уточнения, что уклон статьи - Ext JS. Исходя из этого мне не совсем понятен смысл Вашей мысли на счет "в отсутствие необходимых инструментов для органиции подобных структур".

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

              Подводя итог, мыслю в следующем ключе - согласен со всем Вами сказанным, но ощущаю какой-то выраженный протест. Я где-то чего-то не так понимаю? :)
                +1
                >…ощущаю какой-то выраженный протест. Я где-то чего-то не так понимаю? :)

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

                >…мне не совсем понятен смысл Вашей мысли…
                >…следовало добавить "отсебятину" в виде уточнения, что уклон статьи - Ext JS.

                Да, точно – так и надо было. Я-то все воспринял в контексте: "большинство web-приложений состоят из большого числа библиотек, виджетов и сниппетов из многих и многих источников" (со всеми "вытекающими").
            • НЛО прилетело и опубликовало эту надпись здесь
                +1
                по моему, гораздо удобнее использовать конструкции типа:

                var App = {

                form : {
                foo : function () {
                }
                },

                data : {
                },

                qux : function () {
                }

                }


                p.s.: как-то странно хабр работает с тегом <code>
                  0
                  В частном случае, пожалуй. А вот например, разрабатываете вы свой набор компонент для... для чего угодно. В каждом из скриптов, в самом начале, Вы устанавливаете некий неймспейс, предположим shutnikCmps.nameOfComponent т.к. области видимости кода компонентов также следует разделять. Такой фрагмен, который Вы привели, использовать уже не получится - второй и все последующие из включаемых компонент будут перезаписывать shutnikCmps. Т.о. сначала надо проверять существование верхних уровней пространств. Нет? Или хабр сильно что-то поел и я отвечаю не по существу?
                  0
                  я тож немного поупражнялся :)

                  function package(p){
                  var objs=p.split('.');
                  var cobj=(window[objs[0]]=window[objs[0]]||{});
                  for(var i=1,l=objs.length; i<l ;i++)
                  cobj=(cobj[objs[i]]=cobj[objs[i]]||{});
                  }
                  package("my.pack.age");
                  //console.dir(my);
                  my.pack.age.func=function(){
                  alert('hi');
                  };
                  my.pack.age.func();
                    0
                    Если не хотите привязываться к какому-то фреймворку, для реализации пространства имен можно написать нечто вроде этого:

                    var someRootNamespace = new Object();

                    someRootNamespace.provide = function(object_name)
                    {
                    var objects = object_name.split('.');

                    var object = window;
                    for (var i=0; iЕсли не хотите привязываться к какому-то фреймворку, для реализации пространства имен можно написать нечто вроде этого:

                    var someRootNamespace = new Object();

                    someRootNamespace.provide = function(object_name)
                    {
                    var objects = object_name.split('.');

                    var object = window;
                    for (var i=0; i<objects.length; i++) {

                    if (0 == i && !window[objects[i]])
                    window[objects[i]] = new Object();
                    else if (0 < i && !object[objects[i]])
                    object[objects[i]] = new Object();

                    object = object[objects[i]];
                    }
                    }


                    После выполнения данных строк, в любом месте js-файлов можно писать
                    someRootNamespace.provide('SomeCompany.SomeBigNamespace.SomeBigSubnamespace');
                      0
                      Что-то хабр запорол всё
                      +1
                      Использую не только во избежание конфликтов, но и для разделения функций инициализации скрипта. Сейчас пользуюсь схемой — namespace на страницу + один для общих функций.

                      Все это сложено в один файл + на каждой странице iniline скрипт содержания active_namespace = "Home". Т.е. по DOMReady будет вызвана Home.init()
                        0
                        А в идеале вообще никаких переменных не надо: создали обработчики, повесили их на DOM и всё.
                          0
                          Наверное, возможно, лично я если честно, не пробовал :). Вопрос возникает в самом начале: стоит ли? А как у Вас с этим дело?
                            0
                            Иногда это невозможно, согласен. Но в моей практике в большинстве случаев скрипт просто должен что-то на странице найти, изменить, добавить event handlers и всё. Такой скрипт не должен использовать global scope вообще.
                              0
                              Вот мне хороший урок на будущее. Судя по всему, Вы уже второй человек, которого я непреднамеренно ввел в заблуждение (см. выше) с названием топика. Просто специфика Ext JS, должно быть Вы в курсе, это обычно большие объемы не самого простого кода. Т.е. не описываемый Вами случай.

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

                        Самое читаемое