Metaprogramação em JavaScript: quando o código escreve a si mesmo
\n\nImagine que você está escrevendo um programa capaz de analisar e modificar seu próprio comportamento durante a execução. Parece ficção científica? Bem-vindo ao mundo da metaprogramação em JavaScript!
\n\nEsta técnica permite que o desenvolvedor vá além do código comum e comece a gerenciar a própria estrutura dos objetos, interceptar chamadas de funções e até criar propriedades virtuais que não existem na memória. Neste artigo, vamos explorar os três pilares da metaprogramação: Proxy, Reflect e Symbol. Você aprenderá como usá-los para construir APIs inteligentes, implementar cálculos preguiçosos e proteger dados contra alterações acidentais.
\n\nSe você já domina o básico do JavaScript e quer passar para o próximo nível, este material é para você. Não vamos revisar fundamentos; vamos direto às técnicas avançadas com exemplos reais de código.
\n\nO que é metaprogramação e por que ela é necessária?
\n\nNa programação clássica, escrevemos código que opera sobre dados. Na metaprogramação, escrevemos código que opera sobre outro código ou sobre a estrutura de objetos. Isso oferece possibilidades fantásticas:
\n\n- \n
- Validação e segurança — verificar automaticamente quais valores são atribuídos às propriedades. \n
- Registro e depuração — interceptar qualquer operação com o objeto e exibi-la no console. \n
- Propriedades virtuais — criar propriedades que são calculadas em tempo real, sem ocupar memória. \n
- Criação de DSL (linguagens específicas de domínio) — construir cadeias de chamadas, como no jQuery ou Vue. \n
O JavaScript fornece três ferramentas poderosas para isso. Vamos analisar cada uma delas.
\n\nProxy: seu espião pessoal no mundo dos objetos
\n\nProxy é um objeto wrapper que permite interceptar e modificar operações fundamentais em outro objeto (o objeto alvo). Você pode "ouvir" a leitura de propriedades, escrita, exclusão, chamadas de funções e até a verificação de existência de uma propriedade com o operador in.
A sintaxe é simples: new Proxy(target, handler), onde handler é um objeto com funções de interceptação (traps).
Exemplo 1: Validação de dados com Proxy
\n\nSuponha que temos um objeto de usuário e queremos que a idade seja apenas um número entre 0 e 120, e o nome seja uma string não vazia. Sem o Proxy, teríamos que escrever getters e setters para cada campo. Com o Proxy, isso é feito de forma elegante:
\n\nconst user = {\n name: 'Alice',\n age: 25\n};\n\nconst validator = {\n set(target, property, value) {\n if (property === 'age') {\n if (!Number.isInteger(value)) {\n throw new TypeError('A idade deve ser um número inteiro');\n }\n if (value < 0 || value > 120) {\n throw new RangeError('Idade fora do intervalo permitido');\n }\n }\n if (property === 'name') {\n if (typeof value !== 'string' || value.trim().length === 0) {\n throw new TypeError('O nome deve ser uma string não vazia');\n }\n }\n // Se tudo estiver ok, salva o valor\n target[property] = value;\n return true; // informa que a operação foi bem-sucedida\n }\n};\n\nconst proxiedUser = new Proxy(user, validator);\n\n// Testando\nproxiedUser.age = 30; // OK\nconsole.log(proxiedUser.age); // 30\n\nproxiedUser.age = -5; // Erro: RangeError\nproxiedUser.name = ''; // Erro: TypeError\n\nObserve: interceptamos a operação de atribuição e adicionamos nossa própria lógica. Isso é metaprogramação em JavaScript em sua forma mais pura — código que gerencia o comportamento de outro código.
\n\nExemplo 2: Cálculos preguiçosos e propriedades virtuais
\n\nImagine que temos um