Closure (“clausura” em português) é uma função que tem acesso as variáveis do escopo pai, ou seja, do “escopo léxico” que ela está contida (ex: o corpo de uma função, um bloco delimitador). O closure tem três cadeias de escopo: o seu próprio escopo; escopo externo, tendo acesso as variáveis da função exterior; e o escopo que tem acesso as variáveis globais.
Em JavaScript apenas funções possuem um novo contexto léxico.
Um exemplo clássico de um Closure é a criação de um contador sem o uso de uma variável global. Por ex:
Se você tentasse fazer algo do tipo:
1 2 3 4 5 6 7 8 9 |
function add() { var counter = 0; counter += 1; return counter; } add(); add(); add(); |
Você poderia pensar que o valor de counter no final seria 3. Mas não, ele será sempre 1.
Mas se retornarmos um “função” no lugar de “counter”, teremos assim nossa closure.
1 2 3 4 5 6 7 8 9 10 |
var add = (function () { var counter = 0; return function () {return counter += 1;} })(); // já executa a função anônima aqui // e retorna para a variável add a função // function () {return counter += 1;} add(); add(); add(); |
Dessa forma a variável add possui a função function () {return counter += 1;}
com acesso ao escopo da função pai (onde foi declarado a variável counter).
Portanto quando chamamos add(), estamos chamando a função function () {return counter += 1;}
, que por sua vez incrementa a variável ‘counter’.
Não muito semelhante, mas equivalente, algo parecido, também possibilita ter funções com variáveis privadas, como no exemplo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
function MyObject() { this.public = { a: "aqui é público" } var private = { a: "aqui é privado" } this.get_a = function() { return private.a; } this.set_a = function(x) { // a variável 'private' pode ser alterada apenas através um método private.a = x; } } var obj = new MyObject(); console.log(obj.public); // "aqui é público" -> Acessível console.log(obj.private); // undefined -> Inacessível através da instancia console.log(obj.get_a()); // "aqui é privado" obj.set_a("aqui é privado mesmo") console.log(obj.get_a()); // "aqui é privado mesmo" |
Como demonstrado no exemplo acima, a variável private, “vive” apenas dentro da função e apenas de lá que seu valor pode ser acessado e alterado.
Closure para fazer currying
Currying é uma técnica para transformar uma função que recebe múltiplos parâmetros em uma outra com muito menos parâmetros.
Outra explicação muito boa retirada do insubstituível stackoverflow:
“Currying” é um função que “captura” alguns parâmetros para outras funções e retorna uma função (uma “closure”) que aceita o restante dos parâmetros.
Imagine a função greet:
1 2 3 4 |
var greet = function(greeting, name) { console.log(greeting + ", " + name); }; greet("Hello", "Heidi"); //"Hello, Heidi" |
Com um pequeno ajuste poderíamos fazer uma função para cumprimentar qualquer pessoa sem ter que passar o valor “Hello” para o argumento novamente.
1 2 3 4 5 6 7 8 9 |
var greetCurried = function(greeting) { return function(name) { console.log(greeting + ", " + name); }; }; var greetHello = greetCurried("Hello"); greetHello("Mateus"); //"Hello, Mateus" greetHello("Padua"); //"Hello, Padua" |
Valeu,
Abaixo segue as fontes de onde me inspirei para fazer este post:
https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Guide/Closures
https://www.w3schools.com/js/js_function_closures.asp
https://pt.stackoverflow.com/questions/1859/como-funcionam-closures-em-javascript/1860#1860
https://www.turbosite.com.br/blog/entenda-o-que-sao-closures-em-javascript/
https://www.sitepoint.com/currying-in-functional-javascript/