Во многих языках программирования существуют механизмы, позволяющие скрывать, например, методы и свойства классов. В JavaScript нет встроенных средств, позволяющих, без дополнительных усилий, достигать таких эффектов. Однако подобные вещи можно имитировать с использованием других возможностей языка.
Материал, перевод которого мы сегодня публикуем, посвящён разбору паттерна проектирования «Модуль», который позволяет скрывать приватную информацию в замыканиях, давая доступ лишь к тому, что решил сделать общедоступным разработчик. Эта статья предназначена, в основном, для начинающих программистов — для тех, кто, вроде бы, знаком с такими вещами, как замыкания и IIFE, но пока не особенно уверенно ими пользуется.
IIFE
Управлять областью видимости переменных в JavaScript можно, пользуясь паттерном «Модуль». Для того чтобы создать приватную область видимости, можно воспользоваться замыканием. Как известно, функции создают собственные области видимости, содержимое которых отделено от глобальной области видимости:
(function () {
// здесь находится приватная область видимости
})();
Перед нами — так называемая самовызывающаяся функция (IIFE, Immediately-Invoked Function Expression, немедленно вызываемое функциональное выражение). Такая функция выполняется сразу же после её объявления. Подобные функции удобно использовать для того, чтобы решить некую задачу, которую нужно решить лишь один раз, не оставляя при этом ничего лишнего в глобальной области видимости. Внутри этой функции (как, впрочем, и внутри других функций) создаётся приватная область видимости, недоступная извне. То есть, если объявить внутри этой области видимости другую функцию, то, после того, как IIFE выполнится, доступ к ней получить не удастся.
(function () {
var myFunction = function () {
// выполняем здесь некие действия
};
})();
Попробуем теперь обратиться к функции myFunction
из основного текста программы:
myFunction(); // Uncaught ReferenceError: myFunction is not defined
Как видите, что вполне ожидаемо, этот вызов приводит к ошибке. Это говорит нам о том, что даннаяфункция в той области видимости, из которой мы пытаемся к ней обратиться, недоступна. На самом деле, в двух вышеприведённых примерах ничего полезного не делается. Эти примеры нужны нам лишь для того, чтобы подготовиться к разбору паттерна «Модуль».
Возврат объекта из IIFE и API модуля
Как сделать так, чтобы к функции, объявленной внутри другой функции, всё же, можно было бы обратиться? Собственно говоря, то, о чём мы сейчас будем говорить, и есть паттерн «Модуль». Рассмотрим следующий пример.
// Объявим модуль
var Module = (function () {
return {
myMethod: function () {
console.log('myMethod has been called.');
}
};
})();
// Вызовем функцию как метод объекта
Module.myMethod();
Можно заметить, что здесь используется такое же IIFE, как раньше, но из функции теперь возвращается объект с методом, к которому можно обратиться из глобальной области видимости. Сам по себе, естественно, этот метод вызвать нельзя. Надо отметить, что в этом примере возможностями замыкания мы не пользуемся, об этом мы поговорим ниже.
Объект, возвращаемый из IIFE — это обычный объект, у которого может быть множество методов и свойств. Они формируют общедоступный интерфейс или API модуля.
// Объявим модуль
var Module = (function () {
return {
myMethod: function () {
},
someOtherMethod: function () {
}
};
})();
// Вызовем функцию как метод объекта
Module.myMethod();
Module.someOtherMethod();
Приватные переменные и функции, хранящиеся в замыкании
Теперь пришло время поговорить о приватных переменных и функциях. Например, это могут быть некие вспомогательные функции, обеспечивающие работу внутренних механизмов модуля.
Это могут быть временные переменные, или переменные, играющие роль хранилищ неких данных, доступ к которым мы хотим жёстко контролировать. Нас интересует такое устройство модуля, когда внешнему миру доступно лишь то, что должно быть доступно, а всё остальное оказывается скрытым. Собственно говоря, приватным станет всё то, что, в нашем примере, будет объявлено за пределами объекта, возвращаемого из IIFE.
var Module = (function () {
var privateMethod = function () {
};
return {
publicMethod: function () {
}
};
})();
Метод publicMethod
из этого примера можно вызвать извне, а функцию privateMethod
— нет, так как она находится в приватной области видимости, в замыкании. Именно подобные функции, недоступные извне, могут выполнять роль вспомогательных механизмов модулей. Они могут использоваться для управления внутренними структурами данных, для выполнения каких-то вызовов к неким сервисам, и в других ситуациях.
При работе с подобными функциями нужно учитывать, что к ним можно обращаться из других функций, объявленных в той же области видимости, в том числе — и из методов возвращённого из IIFE объекта, причём, даже после того, как выполнена команда return
, возвращающая этот объект. То есть, общедоступные методы имеют доступ к приватным функциям, они могут с ними взаимодействовать, но в глобальной области видимости эти приватные функции недоступны.
var Module = (function () {
var privateMethod = function () {
};
return {
publicMethod: function () {
// у этого метода есть доступ к privateMethod, мы можем вызвать его здесь так:
// privateMethod();
}
};
})();
Благодаря этому мы можем защищать код от несанкционированного вмешательства и защищать глобальную область видимости от загрязнения. Если этого не делать, то, с одной стороны, работа внутренних механизмов модулей может быть случайно или целенаправленно нарушена, из-за того, что внешний код обращается к функциям или переменным, к которым он обращаться не должен. С другой стороны, если не пользоваться описанным здесь подходом, в глобальную область видимости попадает много ненужного, что может, например, привести к конфликтам имён.
Вот пример объекта, возвращаемого из IIFE, который содержит общедоступные методы и может обращаться к приватным функциям:
var Module = (function () {
var myModule = {};
var privateMethod = function () {
};
myModule.publicMethod = function () {
};
myModule.anotherPublicMethod = function () {
};
return myModule; // возвращает объект с общедоступными методами
})();
// использование модуля
Module.publicMethod();
Именование приватных и общедоступных функций
Существует одно соглашение, в соответствии с которым в начале имён приватных функций ставят знак подчёркивания. Это позволяет, лишь взглянув на код, понять, какие функции являются внутренними, а какие — общедоступными. Например, выглядеть это может так:
var Module = (function () {
var _privateMethod = function () {
};
var publicMethod = function () {
};
return {
publicMethod: publicMethod,
}
})();
Итоги
В этом материале мы рассмотрели простой паттерн «Модуль», который, за счёт использования замыкания, формируемого немедленно вызываемым функциональным выражением, и возвращаемого из этого выражения объекта, позволяет создавать общедоступные методы и приватные функции и структуры данных, с которыми нельзя напрямую работать извне. Этот шаблон позволяет скрывать детали реализации модулей, защищая их от случайных или намеренных изменений, и помогает поддерживать в чистоте глобальную область видимости.
Уважаемые читатели! Пользуетесь ли вы паттерном «Модуль» в своих JS-проектах?
Автор: ru_vds