Инкапсуляция с помощью замыканий в JavaScript

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

Если переменная или объект в JavaScript не помещены внутрь какой-либо функции, то они становятся глобальными. Все глобальные элементы в JavaScript являются свойствами глобального объекта. Для браузера это объект window. При этом конструкции for, if и другие не влияют на видимость переменных.

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

Например, если написать библиотеку следующего содержания foo.js:

var a ="Привет";

function Hello2(){ 
    alert(a); 
}

Hello2(); 


А потом подключить её к проекту:

<script>
    var a = "Пока"; 
</script>

<script src="foo.js"></script> 

<script> 
    alert(a); 
</script> 

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

Инкапсуляция - это помещение всех данных и функций, объединенных одним смыслом в единую сущность. При этом не нужные для пользователя данные скрываются внутри сущности и доступны только внутри неё.
В объектно-ориентированных языках, инкапсуляция достигается благодаря применению модификаторов доступа. В JavaScript для организации инкапсуляции применяются замыкания.
Замыкание - это функция + все внешние переменные, которые ей доступны.
Доступ к переменных в функциях осуществляется следующим образом. Все переменные внутри функции в JavaScript - это свойства объекта LexicalEnvironment, так называемое лексическое окружение. Данный объект является внутренним и к нему нет доступа.

Кроме того, в специальном внутреннем свойстве у функции хранится объект [[scope]]. В нём хранятся внешние для данной функции переменные. Функция сначала ищет переменные в LexicalEnvironment и если не находит, ищет в [[scope]]. Таким образом, функция сначала ищет объявления нужных элементов внутри себя, и лишь потом обращается к внешним элементам. При этом из внешней области берётся именно текущее значение переменной. Кроме того, в JavaScript внутри функций можно объявлять и другие функции.

Ярким примером замыканий может служить код счётчика:

function useCounter() {
    var Count = 1;

    return function() {
        return Count++;
    }; 
} 

var counter1 = useCounter();
alert(counter1()); // 1
alert(counter1()); // 2
alert(counter1()); // 3

var counter3 = useCounter();
alert(counter3()); // 1

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

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

function Count2() {
    var count = 1;

    return { 
        getValue: function() {
            return count ++;
        }, 

        setValue: function(value) {
            count = value;
        }, 

        reset: function() {
            count = 1;
        } 
    }; 
} 

var counter33 = Count2();

alert(counter33.getValue()); // 1
alert(counter33.getValue()); // 2

counter33.setValue(7); 
alert(counter33.getValue()); // 7

Таким образом замыкания можно использовать и внутри объектов.

Для реализации модулей можно использовать мгновенно вызванную функцию (immediately invoked function):

(function() {
    var msg = "hi"; 


    function showmsg () { 
        alert(msg); 
    } 

    // выводим сообщение 
    showmsg(); 
})(); 

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