Проблема придания уникальности названиям пространств имен, таким как com.davidflanagan.Class, влечет за собой другую проблему – увеличение длины имен функций, например com.davidflanagan.Class.define(). Это полное имя функ_ ции, но совсем не обязательно постоянно вводить его вручную всякий раз, когда в этом возникнет необходимость. Поскольку функции в JavaScript являются обычными данными, существует возможность сохранить ссылку на функцию в переменной с любым именем. Например, после загрузки модуля com.davidflana_ gan.Class пользователь модуля может вставить такую строку:
// Более простое для ввода имя.
var define = com.davidflanagan.Class.define;
Использование пространств имен для предотвращения конфликтов – это обязан_ ность, лежащая на плечах разработчика. Но пользователь модуля обладает пре_ рогативой импортировать символы из пространства имен модуля в глобальное
10.2. Импорт символов из пространств имен
пространство имен. Программисту, применяющему модуль, уже известно, ка_ кие модули он задействует и какие потенциальные конфликты имен возможны. Он в состоянии определить, какие символы и как импортировать, чтобы избе_ жать конфликтов имен.
Обратите внимание: в предыдущем фрагменте используется глобальный символ define для представления вспомогательной функции определения классов. Это не очень описательное имя для глобальной функции, поскольку по такому имени трудно сказать, что именно она определяет. Более предпочтительным будет имя:
var defineClass = com.davidflanagan.Class.define;
Но такое изменение имен методов – тоже не лучший выход. Другой програм_ мист, который пользовался этим же модулем ранее, может прийти в недоумение, встретив имя defineClass(), потому что он знаком с другой функцией – define(). Нередко разработчики модулей вкладывают определенный смысл в имена своих функций, и изменять эти имена несправедливо по отношению к модулям. Дру_ гой способ заключается в отказе от использования глобального пространства имен и импорте символов в пространства имен с более короткими именами:
// Создать простое пространство имен. При этом нет необходимости выполнять проверку
// на наличие ошибок, т. к. пользователь знает, какие символы существуют, а какие нет. var Class = {};
// Импортировать символ в новое пространство имен.
Class.define = com.davidflanagan.Class.define;
Существует несколько моментов, которые связаны с импортом символов и кото_ рые необходимо понимать. Первый момент: допускается импортировать толь' ко те символы, которые являются ссылками на функции, объекты или масси' вы. Если импортируется символ, представляющий значение элементарного ти_па, такого как число или строка, тем самым создается статическая копия этого значения. Любые изменения такого значения, выполняемые в пределах про_ странства имен, никак не сказываются на импортированной копии. Предполо_ жим, что метод Class.define() обслуживает счетчик классов, которые определя_ ются с его помощью, и наращивает значение com.davidflanagan.Class.counter при каждом вызове. Если попытаться импортировать это значение, будет просто соз_ дана статическая копия текущего значения счетчика:
// Всего лишь создает статическую копию. Изменения в пространстве имен не будут отражаться
// на импортированном свойстве, поскольку это значение принадлежит элементарному типу. Class.counter = com.davidflanagan.Class.counter;
Это урок для разработчиков модулей – если предполагается объявлять внутри модуля свойства со значениями элементарных типов, необходимо реализовать методы доступа к ним, чтобы эти методы можно было импортировать:
// Свойство элементарного типа, оно не должно импортироваться com.davidflanagan.Class.counter = 0;
// Это метод доступа, который можно импортировать com.davidflanagan.Class.getCounter = function() {
return com.davidflanagan.Class.counter;
}
Второй важный момент, который необходимо понимать, – модули создаются для пользователей модулей. Разработчики модулей всегда должны указывать
206 Глава 10. Модули и пространства имен
полные имена своих символов. Следование этому правилу можно наблюдать
в только что продемонстрированном методе getCounter(). Поскольку JavaScript не обладает встроенной поддержкой модулей и пространств имен, сокращения здесь неуместны и необходимо указать полное имя свойства counter, даже при том, что метод доступа getCounter() принадлежит тому же пространству имен. Разработчи_ ки модулей не должны полагаться на то, что их функции будут импортироваться
в глобальное пространство имен. Функции, которые вызывают другие функции модуля, для корректной работы должны использовать полные имена, даже если не предполагается возможность импорта функции. (Исключением из этого пра_ вила являются замыкания, о чем рассказывается в разделе 10.2.2.)