Closures Avançadas: Módulo Pattern, IIFE e Encapsulamento Real na Prática Já leu

Closures: O Fundamento Invisível Antes de explorar padrões avançados, precisamos entender que closures são funções que "lembram" do escopo em que foram criadas, mesmo após a função externa retornar. Em JavaScript, toda função cria um closure automático. Esse mecanismo é a base de tudo que você verá neste artigo. A variável existe em um escopo privado que apenas pode acessar. Isso é encapsulamento genuíno — não é apenas uma convenção, é garantido pelo motor JavaScript. IIFE: Isolation Imediata O que é IIFE? IIFE (Immediately Invoked Function Expression) é uma função anônima executada no instante de sua definição. Ela cria um escopo isolado que impede poluição do escopo global e conflitos de variáveis. IIFE com Retorno e Parâmetros IIFEs não servem apenas para isolamento — podem retornar objetos com métodos públicos enquanto mantêm dados privados: Falha ${tentativas}. Tente novamente. Module Pattern: Arquitetura Escalável Estrutura Base O Module Pattern combina closures e IIFE para criar módulos com API clara. Cada módulo encapsula

Closures: O Fundamento Invisível

Antes de explorar padrões avançados, precisamos entender que closures são funções que "lembram" do escopo em que foram criadas, mesmo após a função externa retornar. Em JavaScript, toda função cria um closure automático. Esse mecanismo é a base de tudo que você verá neste artigo.

function contador() {
  let numero = 0;

  return function incrementar() {
    return ++numero; // Acessa 'numero' do escopo externo
  };
}

const meuContador = contador();
console.log(meuContador()); // 1
console.log(meuContador()); // 2
console.log(meuContador()); // 3
// numero está protegido, não pode ser acessado diretamente

A variável numero existe em um escopo privado que apenas incrementar pode acessar. Isso é encapsulamento genuíno — não é apenas uma convenção, é garantido pelo motor JavaScript.

IIFE: Isolation Imediata

O que é IIFE?

IIFE (Immediately Invoked Function Expression) é uma função anônima executada no instante de sua definição. Ela cria um escopo isolado que impede poluição do escopo global e conflitos de variáveis.

(function() {
  const varivelPrivada = "Apenas eu acesso isso";
  console.log(varivelPrivada);
})();

// varivelPrivada não existe aqui
console.log(typeof varivelPrivada); // "undefined"

IIFE com Retorno e Parâmetros

IIFEs não servem apenas para isolamento — podem retornar objetos com métodos públicos enquanto mantêm dados privados:

const usuario = (function() {
  const senha = "super_secreto"; // Privada
  let tentativas = 0;

  return {
    login: function(senhaInformada) {
      tentativas++;
      if (senhaInformada === senha) {
        return "Login bem-sucedido!";
      }
      return `Falha ${tentativas}. Tente novamente.`;
    },
    getTentativas: function() {
      return tentativas;
    }
  };
})();

console.log(usuario.login("errada"));    // Falha 1. Tente novamente.
console.log(usuario.login("super_secreto")); // Login bem-sucedido!
console.log(usuario.getTentativas());    // 2
console.log(usuario.senha);              // undefined — realmente privada!

Module Pattern: Arquitetura Escalável

Estrutura Base

O Module Pattern combina closures e IIFE para criar módulos com API clara. Cada módulo encapsula lógica e expõe apenas o que é necessário:

const contaBancaria = (function() {
  // PRIVADO
  const contas = {};
  const taxa = 0.02;

  function calcularJuros(valor) {
    return valor * (1 + taxa);
  }

  function validarConta(numero) {
    return numero && numero.length === 10;
  }

  // PÚBLICO
  return {
    criarConta: function(numero, saldoInicial = 0) {
      if (!validarConta(numero)) {
        throw new Error("Número de conta inválido");
      }
      contas[numero] = { saldo: saldoInicial };
      return `Conta ${numero} criada com R$${saldoInicial}`;
    },

    depositar: function(numero, valor) {
      if (!contas[numero]) return "Conta não existe";
      contas[numero].saldo += valor;
      return `Novo saldo: R$${contas[numero].saldo}`;
    },

    sacar: function(numero, valor) {
      if (!contas[numero]) return "Conta não existe";
      if (contas[numero].saldo < valor) return "Saldo insuficiente";
      contas[numero].saldo -= valor;
      return `Saque realizado. Saldo: R$${contas[numero].saldo}`;
    },

    aplicarJuros: function(numero) {
      if (!contas[numero]) return;
      contas[numero].saldo = calcularJuros(contas[numero].saldo);
    },

    consultarSaldo: function(numero) {
      return contas[numero]?.saldo ?? "Conta não encontrada";
    }
  };
})();

contaBancaria.criarConta("1234567890", 1000);
console.log(contaBancaria.depositar("1234567890", 500));    // Novo saldo: R$1500
contaBancaria.aplicarJuros("1234567890");
console.log(contaBancaria.consultarSaldo("1234567890"));    // 1530
console.log(contaBancaria.taxa);                             // undefined — privado!

Vantagens do Module Pattern

O padrão acima alcança três objetivos críticos: os dados (contas, taxa) são absolutamente privados; funções auxiliares (calcularJuros, validarConta) não poluem o escopo global; e apenas a API intencional (criarConta, depositar, etc.) é exposta. Não é possível acessar ou modificar dados internos sem passar pelos métodos públicos.

Encapsulamento Real vs. Falso

Diferenças Práticas

Muitos desenvolvedores confundem privacidade com convenção. Um underscore (_variavel) é apenas uma sugestão — não impede acesso real:

// Falso encapsulamento (convenção)
class Pessoa {
  constructor(nome) {
    this._nome = nome; // Convenção: "é privado"
  }
  getNome() {
    return this._nome;
  }
}

const p = new Pessoa("João");
console.log(p._nome);     // "João" — consegui acessar!
p._nome = "Hacker";       // Consegui modificar!
console.log(p.getNome()); // "Hacker"

Compare com encapsulamento genuíno usando closures:

// Encapsulamento real
function Pessoa(nome) {
  let _nome = nome; // Realmente privado — sem underscore necessário

  this.getNome = function() {
    return _nome;
  };

  this.setNome = function(novoNome) {
    if (novoNome && novoNome.length > 0) {
      _nome = novoNome;
    }
  };
}

const p = new Pessoa("João");
console.log(p._nome);      // undefined — não existe
console.log(p.getNome());  // "João"
p._nome = "Hacker";        // Cria propriedade nova, não afeta _nome real
console.log(p.getNome());  // "João" — imutável!
p.setNome("Maria");
console.log(p.getNome());  // "Maria" — mudou apenas através do setter

A diferença é fundamental: closures criam verdadeira privacidade no nível de linguagem, não apenas em convenção.

Conclusão

Você aprendeu três conceitos essenciais: closures são o mecanismo que permite que funções acessem variáveis de seus escopos externos, criando a base para privacidade real em JavaScript. IIFEs isolam código imediatamente, evitando conflitos globais e permitindo retornar APIs públicas com dados privados. O Module Pattern escala esses conceitos para criar arquiteturas mantíveis com encapsulamento genuíno, onde dados internos são absolutamente protegidos e apenas a interface intencional é exposta. Dominar esses padrões transforma você de programador que escreve código em engenheiro que projeta sistemas robustos.

Referências


Artigos relacionados