How Do Closures Work in JavaScript?

106

I've always wanted to know how Closures work in JavaScript, I've read some definitions but never quite understood.

Could you give me a simple, objective but content explanation?

    
asked by Elias Developer 11.01.2014 в 02:43

5 answers

73

Closure (

In JavaScript, only functions define a new lexical context (other languages have different rules - some even support the concept of closure):

var a = 10; // Mesmo "a" para script1.js, script2.js, etc (efetivamente, uma global)
function f() {
    var b = 20; // Um "b" diferente para cada invocação de f
    if ( x ) {
        var c = 30; // Mesmo "c" dentro e fora do if (i.e. o contexto é "f", não o bloco if)

And each new context created within an existing context has access to all the variables defined in the "outer" :

>
function x(a1) {          // "x" tem acesso a "a"
    var a2;
    function y(b1) {      // "y" tem acesso a "a" e "b"
        var b2;
        function z(c1) {  // "z" tem acesso a "a", "b", e "c"
            var c2;

It is important to note that when the internal function will run, nor what value the external variables had at the time the object function was created (by contrast with the definition of the function, which is at compile / interpretation time). What matters is that both share the same variable, and written on one side will reflect on each other's readings and vice versa.

Pitfalls

An example of a common error involving closures is the creation of a function within a for block:

for ( var i = 0 ; i < elementos.length ; i++ ) {
    elementos[i].onclick = function() {
        alert("Esse é o elemento " + i);
    }
}

This code does not work as expected, since the i variable used by the anonymous function is the same i of the external context - which means that when the i external changes, the value that the internal function going to access is different. At the end, i will equal elementos.length (ex: 10 ), so clicking on any element will always print "That's element 10".

A possible solution to this problem is to create a new lexical context that "captures" the value of that variable at the desired moment:

for ( var i = 0 ; i < elementos.length ; i++ )
    (function(i) {
        elementos[i].onclick = function() {
            alert("Esse é o elemento " + i);
        }
    })(i);

In this way, the i function parameter is not the same as i used by the for loop - and it has value that the variable had at execution time .

Utility

There are many advantages to using closures , as exemplified in Jordan's response (which demonstrates a way to implement currying ). Another example would be to simulate private variables - something that is not typically supported by the JavaScript language:

function MeuObjeto() {
    this.publico = { ... }
    var privado = { ... }
    this.foo = function() {
        return privado.a;
    }
    this.bar = function(x) {
        privado.a = x;
    }
    ...
}
var obj = new MeuObjeto();
obj.publico; // Acessível
obj.privado; // undefined

Note that because there is no direct reference to privado , this object can not be manipulated directly (only indirectly through foo and bar ). But since foo and bar were created within the lexical context of the constructor, they have access to the locale variables of the constructor, and can access them normally.

Another "classic" example is the Accumulator Generator , quoted num article by Paul Graham where the power of relative expressiveness of the various programming languages is discussed. The requirement is simple:

  

Write a function foo that receives a number n and returns a function that receives a number i , and returns n incremented i .

     

Note: (a) [argument] is a number, not an integer. (b) is incremented , not more .

The proposed solution, with use examples:

function foo (n) { 
    return function (i) { 
        return n += i;
    } 
}

var x = foo(10);
x(2); // 12
x(3); // 15

var y = foo(20);
y(5); // 25
y(2); // 27

As the examples at the end of the article show, languages that do not support closures end up being much more variant (requiring too much code to do little), so it takes longer to be written, read, may contain more bugs (since the likelihood of bugs increases with the amount of code line), etc.

Note: I've included here part of an answer I gave a similar question (the question was not about closures, but the root cause of the problem was), adapting to the answer become more generic.

    
11.01.2014 в 03:27
43

"Closures" are functions that "capture" variables that come from outside the function. In English it is said that a closure (closes over) certain variables.

A typical example:

function somador(v1) {
  return function(v2) {
    return v1 + v2;
  }
}

somador returns a closure, a function that captures the value of v1 (note that somador is not a closure, but its return value):

var soma10 = somador(10);
var total1 = soma10(5); // total1 é 15, já que soma10 capturou o valor de v1 como "10"

var soma20 = somador(20);
var total2 = soma20(5); // total2 é 25, soma20 capturou v1 = 20

var total3 = somador(30)(5); // usando diretamente a "closure"

"Currying" is a function that "captures" some parameters for other functions and returns a function (a "closure") that accepts the rest of the parameters.

Given the soma function:

function soma(v1, v2) {
  return v1 + v2;
}

And a simple currying function:

function curry(fn /*, argumentos parciais para fn */) {
  var args1 = [].slice.call(arguments, 1); // argumentos após fn
  return function(/* o resto dos argumentos para fn */) {
    var args2 = [].slice.call(arguments);
    return fn.apply(null, args1.concat(args2)); // junta todos os argumentos
  }
}

We can use them like this:

var soma10 = curry(soma, 10); // soma10 capturou "soma" e "10"
var total = soma10(5);
    
11.01.2014 в 02:50
39

What is closure ?

Closure is a programming language concept that, in a simplified way, allows a function to access variables from its parent function.

This technique comes from functional languages, but it was also diffused and implemented for other languages like JavaScript and C #.

Example:

function start() {
    var message = "hello world";

    function display() {
        console.log(message);
    }

    display();
}

In the example, the display function has access to the message variable, but note that the variable was declared in the parent body of the start function. The "in" (inner) function has access to the variables of the "function from outside" (outer). display in the example is considered a closure .

But, even more, the referenced variables of the parent function remain available even at the end of their execution. Here's another example:

function getDisplay() {
    var message = "hello world";    
    return function display() {
        console.log(message);
    };
}

var display = getDisplay();
display();

Note that now the display of the message value is done even after its function ( getDisplay ) has already finished running.

How it works

For this to be possible the language has to provide for function not only references of its local variables and global variables but also the references of non-local variables, which are neither in its scope nor nor in the global scope, but that somehow became accessible to it (as in the example by the function that encapsulates it).

Speaking of JavaScript, in practice, this "environment" of the function is called execution context . For each function call the interpreter assembles a execution context for it, and that's where the magic happens: -)

But what about the garbage collector ?

Traditionally, the memory used by the variable is "freed" to the garbage collector as it finishes its scope, which is usually how the execution exits the block that encapsulates it ( { } ) .

The difference in JavaScript is that your interpreter maintains a stack of execution context as the functions are called. And it is this stack that will provide the interpreter with information whether the scope of the variable has ended or not.

It's worth noting: this stack is not the same memory that we call "stack" in other languages like C / C ++.

Reference, not value

As you may have noticed from most of the examples out there, closure is copied from the references of variables, not their values .

This means that by changing the value of a variable within a closure all other contexts that have reference to this variable will get this value when accessing it. Beware of possible side effects.

    
11.01.2014 в 04:31
28

Closure has to do with the scope of a JavaScript variable.

To avoid polluting the global namespace by protecting your script's variables from mingling with other variables from other scripts, you can use a large closure to put all the code in your plugin, app, or library ...

p>
(function($){
    //  aqui vem o teu código
    var contador;
})(jQuery);

The scope of a closure will always begin with the character { and end with the } character. Yes, that's right: a closure always has a beginning and an end determined by the open-bracket and date-bracket.

What's interesting is that when you create a function at a given point in the code, you automatically create a closure , where the current local variables become available within the new scope:

var crud = (function(){
    var modulo = {},
        contador = 0;

    modulo.update = function() {   // aqui temos o início de outro closure
        contador++;
    };  // e aqui o respectivo final

    return modulo;
})();

Notice the counter variable. We do not have access to it "outside" the crud module. And it also was not defined within the update function. When we call crud.update () for the first time, the variable counter will become 1 . If we call crud.update () a second time, the counter variable will become 2 .

You wanted a simple, objective answer: take a good look at the example above. If you understand the simple fact that we have a variable captured within a scope, but always available to the function that "captured" it, you will have understood the essence of the riddle. It's not complicated. But it's a feature of JavaScript that opens up space for amazing patterns.

    
06.02.2014 в 17:31
0

Closure, in JS, for example - I will give an example of a language - is when the outer scope is seen and saved , from within an inner block or function.

For example,

var minhaUrl = "Produtos/1";

function ajaxeira() {
  $.ajax({
    url: minhaUrl,
    ...
    success: function (data) {
      alert(JSON.stringify(data));
    }
  });
}

At the beginning of execution of this block above, minhaUrl contains "Produtos/1" and is clear when watching it.

For the purpose of illustration, I can demonstrate the use of this function ajaxeira() and change minhaUrl as if it were within the scope of the function,

minhaUrl = "Pedidos/4";
ajaxeira(); // Imprime o pedido 4 em formato JSON, em um alert, se der boa

minhaUrl = "Produtos/11";
ajaxeira(); // Imprime o produto 11 em formato JSON, em um alert, se der boa

In the above case, minhaUrl is a global variable, but I can have a function that holds var minhaUrl and, within this function, have another function, which uses minhaUrl :

$(function () {
    var minhaUrl = "Produtos/1";
    
    function ajaxeira() {
        alert('Imagine aqui a chamada ajax para ' + minhaUrl);
    }
    
    function pedidos4() {
        minhaUrl = "Pedidos/4";
    }
    
    $('#btnPedidos4').on('click', pedidos4);
    $('#btnAjaxeira').on('click', ajaxeira);

    alert(minhaUrl); // Popup com 'Produtos/1'
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><inputtype="button" id="btnAjaxeira" value="Ajaxeira" />
<input type="button" id="btnPedidos4" value="Pedidos 4" />

In the above code, when I click the Request 4 button, I change the value of minhaUrl . Then, if I hit the Ajaxeira button, the popup with the content of the request and not the product will appear, which is the default.

Note also that I am here using pedidos4() and ajaxeira() as callbacks and they also see and save the scope immediately outside their declarations.

    
09.07.2018 в 18:44