Closures são um padrão de programação em JavaScript que permite usar variáveis de um âmbito lexical exterior dentro de um bloco de código aninhado. O JavaScript suporta closures de forma transparente, e são muitas vezes usados sem se saber o que são.
// Top-level declarations are global-scope
const dozen = 12;
// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
return dozen * n;
}
O nome closure deriva historicamente do cálculo λ e foi popularizado pelo Scheme (fonte) para designar as ligações de variáveis abertas e fechadas de uma função.
Privacidade de dados / Encapsulamento de dados
// Consider this function:
const saveNumber = (number) => {
// The saved value is unaccessible by the outer lexical scope.
const value = number;
// We can provide access to the primitive value with a function, but the original will never change
return () => value;
};
// Attempting to set the variable outside of its lexical scope results in an error
value = 42;
Aplicação parcial
// The arguments are applied one-by-one, using closures to arrive at the final result
function partialBuildUri(scheme) {
return function (domain) {
return function (path) {
return `${scheme}://${domain}/${path}`;
};
};
}
// A function could apply them all at once.
function buildUri(scheme, domain, path) {
return partialBuildUri(scheme)(domain)(path);
}
// Or apply a few to create a flexible system of functions for reuse.
function buildHttpsExercismUri(path) {
return partialBuildUri('https')('exercism.org');
}
Em qualquer linguagem de programação, os closures estão estreitamente relacionados com o âmbito das variáveis. Em JavaScript, há 3 tipos de âmbito lexical para variáveis:
var.{...}) com as palavras-chave let e const.Na prática, um closure ocorre muitas vezes quando uma função (ou bloco) usa uma variável de um âmbito exterior. Em JavaScript, isto é suportado de forma transparente, pelo que os closures são frequentemente usados sem referência específica.
A palavra-chave var define uma variável de âmbito de função. Isto significa que as variáveis definidas por var estão disponíveis em qualquer ponto da função onde são definidas e em qualquer âmbito lexical aninhado (quer seja de função, quer de bloco).
{
var five = 5;
}
// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;
As palavras-chave let e const definem variáveis de âmbito de bloco. let define uma variável mutável, cujo valor pode ser definido várias vezes e que pode ser definida mas não inicializada. const define uma variável que tem de ser inicializada quando é definida, e cujo valor não pode depois mudar. Se a variável const for definida com um valor primitivo, não pode ser alterada. Se a variável const for definida com um valor de referência, como um array ou um objeto, a referência não pode mudar, mas o conteúdo do array pode ser alterado.
Considera este bloco:
// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;
{
// This succeeds, as 'two' is available from the outer lexical scope
const oneLess = two - 1;
}
// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);
Embora possamos usar chavetas ({...}) arbitrariamente para criar blocos, é mais frequente encontrá-las em torno de condicionais, funções e classes.
const PI = 3.14;
// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
return PI * radius * radius;
}