Как работают замыкания в JavaScript | Курс ReactJS в Минске

Как работают замыкания в JavaScript

Вопрос:

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

Онлайн курс ReactJS в Минске | ReactWarriors

Ответ:

Это перевод изcommunity wiki.

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

function внешняя(x) {
  var tmp = 3;

  function внутренняя(y) {
    alert(x + y + (++tmp)); // выведет 16
  }
  внутренняя(10);
}
внешняя(2);

Этот код всегда выдаёт 16, потому что функциявнутренняявидитx, который является переменной в функциивнешняя. В данном случае аргументом функции. Так жевнутренняя()может видетьtmpизвнешней().

Это и называется замыкание или closure. Если точнее, замыканием называется именно внешняя функция, а всё что внутри неё называется closure environment или среда замыкания.

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

function foo(x) {
  var tmp = 3;

  return function (y) {
    alert(x + y + (++tmp)); // will also alert 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

Приведённая выше функция также выдаст 16, посколькуbarдаже после завершенияfooпродолжает иметь доступ кxиtmp, пусть даже сама переменнаяbarи не находится внутри области видимости в которой они были объявлены.

При этом, поскольку переменнаяtmpвсё ещё находится внутри замыканияbar, она продолжает увеличиваться при каждом вызовеbar.

Вот простейший пример замыкания:

var a = 10;
function test() {
  console.log(a); // вывод 10
  console.log(b); // вывод 6
}
var b = 6;
test();

При запуске функции в JavaScript, для неё создаётся окружение, то есть список всех видимых ей переменных, не только аргументов и переменных объявленных внутри неё, но и снаружи, в данном примере это ‘a’ и ‘b’.

Можно создать более чем одно замыкание в одном окружении, вернув их массивом, объектом или привязав к глобальным переменным. В таком случае, все они будут работать стем же самымзначениемxилиtmp, не создавая отдельных копий.

Поскольку в нашем примереxэто число, то его значениекопируетсявfooкак его аргументx.

С другой стороны, в JavaScript всегда используются ссылки, когда передаются объекты. Если бы вы вызвалиfooс объектом в качестве аргумента, то возвращённое замыкание вернуло бы ссылку на оригинальный объект!

function foo(x) {
  var tmp = 3;

  return function (y) {
    alert(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    alert(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar теперь замыкание ссылающееся на age.
bar(10);

Как и следовало ожидать, каждый вызовbar(10)увеличиваетx.memb. Чего вы могли не ожидать, так это, чтоxпродолжает ссылаться на тот же самый объект, что иage! После двух вызововbar,age.membбудет равен 2! Кстати, так и происходят утечки памяти в HTML объектах.Источник

Онлайн курс ReactJS в Минске | ReactWarriors

Поделись знаниями: