How closures work in JavaScript


As Albert Einstein said:

If you can't explain something to a six-year-old, I don't think you quite understand it yourself.

I tried to explain the closures to a 27-year-old friend, and he didn't understand anything.

How would you explain closures to someone who is familiar with all the programming principles necessary to understand closures (functions, variables, scope, etc.), except, in fact, closures?

This question is translated from English-language SO.

Author: Pavel Mayorov, 2015-04-07

7 answers

This is a translation from the community wiki.

In JavaScript, functions can be described not only one after another, but also one within another. When you have one function inside another, the internal function has access to the variables of the external function.

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

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

This code always outputs 16, because the function внутренняя sees x, which is a variable in the function внешняя. In this case, the function argument. Similarly, внутренняя() can see tmp from внешней().

This is called a closure or closure. More precisely, the closure is called the external function, and everything inside it is called the closure environment or the closure environment.

Sometimes it is said that a closure is a function that returns a function, this is wrong. In order to call a function a closure, it is sufficient that the internal function accesses the variable from outside its scope.

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);

Given above the function will also return 16, since bar continues to have access to x and tmp even after foo is completed, even if the bar variable itself is not inside the scope in which they were declared.

However, since the tmp variable is still inside the bar closure, it continues to increase each time bar is called.

Here is the simplest example of a closure:

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

When you run a function in JavaScript, an environment is created for it, then there is a list of all the variables visible to it, not only the arguments and variables declared inside it, but also outside it, in this example these are 'a' and 'b'.

You can create more than one closure in the same environment by returning them as an array, object, or binding them to global variables. In this case, they will all work with the same value x or tmp, without creating separate copies.

Since in our example x is a number, its value is copied to in foo as its argument x.

On the other hand, JavaScript always uses references when passing objects. If you called foo with an object as an argument, the returned closure would return a reference to the original object!

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);

As expected, each call to bar(10) increments x.memb. What you might not expect is that x continues to refer to the same object as age! After two calls bar, age.memb will be equal to 2! By the way, this is how memory leaks occur in HTML objects.

 47
Author: exebook, 2020-01-09 19:24:04

Closure - the ability of a function to remember the context in which it was defined and access it during the call. By context, we mean variables that are available at the place of its definition - the function implicitly stores references to them and can then access them-even after the execution of the program has gone beyond the scope of their visibility. In this case, the variables are not collected by the garbage collector, and thanks to these implicit references, they continue to live as long as the function and, accordingly, they can be used by the function.

 20
Author: Вячеслав Лапин, 2015-04-09 08:45:43

That's a good question!
For a long time I myself did not stick what to what, although I actively used them.
Over time, it came (I sincerely hope so).
But in a nutshell, too, you can not tell, here is a good description of their work.
So:
Closures are a distant relative of encapsulation, I guess.
In JS, only functions create the scope. Therefore, when creating any function, it has two special properties (of course, there are more of them, but we do not consider them): [[scope]] (all the created functions are stored there it is not available to the programmer in any way) and __proto__ (the reference to the entity in which the function was created should not be available in any way, you can only influence it through prototype, but some browsers successfully scored on this).
For example, when querying a variable in a function, the interpreter searches for it in the corresponding [[scope]], if it does not find it, it follows the link in __proto__ and searches its scope. If it doesn't find it, it goes through and searches again. And only when he is convinced that he has reached the the end (the main object, window for browsers), gives the final error that it did not find the variable.
Since the external environment does not have access to the internal [[scope]] (it does not even know about them), it is impossible to get to the internal variables externally, when the internal ones easily get to the external ones.

If in a nutshell and rude, then somehow so.

 10
Author: Саша Черных, 2016-05-13 06:28:47

Closure is the availability of variables (scope) for the current function from the parent function in which it was declared

 9
Author: Special, 2015-04-07 09:50:57
function mCounter(){
    var count = 0;
    function counter(){ // создаем функцию и увеличиваем переменную
        count = count + 1 ; // можно +=
        return count;
    }
    return counter; // возрашаем функцию которая использует
 свободную переменную. Это замыкание.
}
var dCount = mCounter();//здесь привызове mCounter() мы получаем замыкание(функцию с окружением)
console.log(dCount());//1  при вызове dCount()
//  значение count берется из переменной count,
// которая находится в окружении замыкания.
console.log(dCount());//2
console.log(dCount());//3
console.log(dCount());//4
//мы вызываем функцию которая создает
// др.функцию и возвращает ее вместе с окружением,
// в нашем случае содержащим свободную переменную count.
// Это и есть замыкание.
//свободная переменная count не связана ни с какими значениями
(потому и наз.свободная)
//т.е. count объявляется за пределами функции counter()
//в окружении, где наша свободная переменная , функция 
становится замкнутой, а
//если взять функцию с ее окружением, получится замыкание.
 3
Author: gorsobes, 2017-06-13 04:54:13

Explain by example.

function func1() {
     var a = Math.random();
     return function () {
            return a;
     }
}

var f = func1();
console.log(f());
var f2 = func1();
console.log(f2());

0.34218455478549004
0.6286177076399326
 1
Author: Oleg, 2015-04-07 10:02:50

An interesting example of closures, like to ask at interviews, can be useful to someone:

function sum(a) {
  const add = function(b) {
    if (b !== undefined) {
      a = a + b;
      return add;
    }
    return a;
  }
  return add;
}

console.log(sum(1)(2)(3)(4)()) //10
 0
Author: Daniil Dubchenko, 2019-07-16 05:53:06