O que são Closures e como eles funcionam?

Closures são um dos recursos mais poderosos e frequentemente mal compreendidos em JavaScript. No seu núcleo, um closure é uma função que tem acesso a variáveis no seu escopo léxico externo envolvente, mesmo depois que a função externa tenha retornado. Isso pode parecer complexo mas é um conceito fundamental que possibilita muitos padrões avançados de programação em JavaScript. Para entender closures, vamos começar com um exemplo:
function outerFunction(x) {
    let y = 10;
    function innerFunction(){
        console.log(x + y);
    }
    return innerFunction;
}

let closure = outerFunction(5);
console.log(closure()); // 15
Neste exemplo, outerFunction recebe um parâmetro x e define uma variável local y. Ele então define uma innerFunction que usa tanto x e y. Finalmente ele retorna innerFunction. Quando chamamos outerFunction(5) ela retorna innerFunction que atribuímos à variável closure. Quando chamamos closure() mais tarde, ele ainda tem acesso a x e y de outerFunction mesmo que outerFunction já tenha terminado de executar. Esta é a essência de um closure. A função interna mantém uma referência ao seu ambiente léxico externo, preservando o acesso às variáveis nesse ambiente mesmo depois que a função externa foi concluída. Closures são particularmente úteis para criar variáveis e funções privadas. Considere este exemplo:
function createCounter() {
    let count = 0;
    return function () {
        count++;
        return count;
    };
}

let counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
Neste caso, createCounter retorna uma função que incrementa e retorna uma variável count. A variável count não é diretamente acessível de fora de createCounter, mas a função retornada (nosso closure) tem acesso a ela. Cada vez que chamamos counter(), ele incrementa e retorna o count. Closures também podem capturar múltiplas variáveis do seu escopo externo. Por exemplo:
function multiply(x) {
    return function (y) {
        return x * y;
    };
}

let double = multiply(2);
console.log(double(5)); // 10
Aqui a função interna captura o parâmetro x de multiply. Quando criamos double chamando multiply(2) ele retorna uma função que sempre multiplica seu argumento por 2. Uma coisa importante a notar sobre closures é que elas capturam variáveis por referência e não por valor. Isso significa que se o valor de uma variável capturada mudar, o closure verá o novo valor. Por exemplo:
function createIncrementer() {
    let count = 0;
    return function () {
        count++;
        console.log(count);
    };
}

let increment = createIncrementer();
increment(); // 1
increment(); // 2
Cada vez que chamamos increment ele está trabalhando com a mesma variável count, não uma cópia do seu valor inicial. Closures são uma ferramenta poderosa em JavaScript. À medida que você continua a trabalhar com JavaScript, você descobrirá que entender e usar closures de forma eficaz pode melhorar muito sua capacidade de escrever código limpo, eficiente e poderoso.
Este módulo não possui perguntas. Marque como concluído.