Замыкания в JavaScript
JavaScript

Замыкания в JavaScript

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

Что такое замыкание

Замыкание — это функция, объявленная внутри другой функции и имеющая доступ к переменным внешней (вмещающей) функции. Замыкание имеет доступ сразу к трем областям видимости:

  • к своей собственной области видимости (переменные, объявленные внутри замыкания);
  • к области видимости внешней функции (переменные, объявленные внутри внешней функции);
  • к глобальной области видимости.

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

Простыми словами замыкание — это функция, описанная внутри другой функции.

Пример замыкания в JavaScript:


function showName(firstName, lastName) {
 
    var nameIntro = "Your name is ";

    function makeFullName() {
        
return nameIntro + firstName + " " + lastName;

    }

    return makeFullName();

}

showName("Michael", "Jackson"); // Your name is Michael Jackson


Замыкания также нередко используются в jQuery:


$(function() {
    var selections = []; 
    $(‘.niners’).click(function() {
        selections.push(this.prop(‘name’));
    });
});

Правила и побочные эффекты замыканий

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

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

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

Замыкания хранят ссылки на переменные внешней функции, а не фактические значения.

Такая интересная особенность позволяет описывать приватные переменные. Это способ впервые был предложен Дугласом Крокфордом:


function user() {
    var name = ‘Unknown’;
    return {
        getName: function() {
            return name;
        },
        setName: function(newName) {
            name = newName;
        }
    }
}

var testUser = user();
testUser.getName(); // Unknown
testUser.setName(‘John Smith’); // Изменяем значение приватной переменной
testUser.getName(); // John Smith

Побочные эффекты, связанные с замыканиями

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


function userIdGenerator(users) {
    var i;
    var uniqueId = 100;

    for (i = 0; i < users.length; i++) {
        users[i]['id'] = function()  {
            return uniqueId + i;
        }
    }

    return users;
}

var testUsers = [{ name: "Smith", id:0 }, { name: "Johnson", id:0 }, { name: "Thompson", id:0 }];
var testUsersIds = userIdGenerator(testUsers);
var firstId = testUsersIds[0];
console.log(firstId.id()); // 103

В предыдущем примере, к тому времени, когда вызывается анонимная функция, значение i становится равно 3 (длина массива). Число 3 было прибавлено к значению переменной uniqueId, тем самым для всех элементов массива testUsers значение id стало равно 103, вместо предполагаемых 100, 101, 102.

Решением предыдущей проблемы может быть шаблон немедленно вызываемой функции (Immediately Invoked Function Expression — IIFE):


function userIdGenerator(users) {
    var i;
    var uniqueId = 100;

    for (i = 0; i < users.length; i++) {
        users[i]['id'] = function(j) {
            return function() {
                return uniqueId + j;
            } ();
        } (i);
    }

    return users;
}

var testUsers = [{ name: "Smith", id:0 }, { name: "Johnson", id:0 }, { name: "Thompson", id:0 }];
var testUsersIds = userIdGenerator(testUsers);

var firstId = testUsersIds[0];


console.log(firstId.id); // 100

var secondId = testUsersIds[1];


console.log(secondId.id); // 101

Заключение

Замыкания — это очень интересный и крайне полезный прием программирования. Суть замыкания можно выразить так: в функции доступны все переменные той области видимости, в которой она была определена.

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

Рассылка
Подпишитесь на рассылку и получайте дайджест новостей и статей.
Никакого спама!
Подписаться