Boas Práticas de Proxy e Reflect em JavaScript: Interceptando Operações em Objetos para Times Ágeis Já leu

Introdução: Por que Proxy e Reflect Importam para Times Ágeis Proxy e Reflect são metaprogramação em JavaScript que permitem interceptar e customizar operações fundamentais em objetos. Para times ágeis que precisam iterar rapidamente, essas APIs são ouro: elas facilitam validação, logging, lazy loading e padrões como Observer sem modificar o código original. A diferença entre um time que consegue refatorar sem medo e outro que não consegue frequentemente está em como lidam com abstrações. Vamos aprender a implementar essas práticas com segurança. Fundamentos de Proxy: Interceptando Operações O que é um Proxy? Um Proxy é um objeto especial que atua como intermediário entre seu código e o objeto alvo, capturando operações como leitura, escrita e deleção de propriedades. O construtor recebe um alvo e um handler (objeto com "armadilhas" ou traps). Acessando propriedade: ${prop} Definindo ${prop} = ${value} Casos de Uso Prático Validação é apenas o começo. Em times ágeis, Proxies reduzem duplicação ao centralizar regras de negócio. Com lazy

Introdução: Por que Proxy e Reflect Importam para Times Ágeis

Proxy e Reflect são metaprogramação em JavaScript que permitem interceptar e customizar operações fundamentais em objetos. Para times ágeis que precisam iterar rapidamente, essas APIs são ouro: elas facilitam validação, logging, lazy loading e padrões como Observer sem modificar o código original. A diferença entre um time que consegue refatorar sem medo e outro que não consegue frequentemente está em como lidam com abstrações. Vamos aprender a implementar essas práticas com segurança.

Fundamentos de Proxy: Interceptando Operações

O que é um Proxy?

Um Proxy é um objeto especial que atua como intermediário entre seu código e o objeto alvo, capturando operações como leitura, escrita e deleção de propriedades. O construtor new Proxy(target, handler) recebe um alvo e um handler (objeto com "armadilhas" ou traps).

const usuario = {
  nome: 'Alice',
  email: 'alice@example.com'
};

const usuarioProxy = new Proxy(usuario, {
  get(target, prop) {
    console.log(`Acessando propriedade: ${prop}`);
    return target[prop];
  },
  set(target, prop, value) {
    console.log(`Definindo ${prop} = ${value}`);
    if (prop === 'email' && !value.includes('@')) {
      throw new Error('Email inválido');
    }
    target[prop] = value;
    return true;
  }
});

usuarioProxy.nome; // "Acessando propriedade: nome" → "Alice"
usuarioProxy.email = 'invalido'; // Error: Email inválido

Casos de Uso Prático

Validação é apenas o começo. Em times ágeis, Proxies reduzem duplicação ao centralizar regras de negócio. Com lazy loading, você carrega dados sob demanda; com logging transparente, consegue debugging sem poluir classes de negócio. O handler suporta múltiplas traps: get, set, has, deleteProperty, apply, construct, e outras.

const db = {
  usuarios: null // será carregado sob demanda
};

const cache = new Proxy(db, {
  get(target, prop) {
    if (prop === 'usuarios' && target.usuarios === null) {
      console.log('Carregando usuários do banco...');
      target.usuarios = [
        { id: 1, nome: 'Alice' },
        { id: 2, nome: 'Bob' }
      ];
    }
    return target[prop];
  }
});

console.log(cache.usuarios); 
// "Carregando usuários do banco..."
// Próximos acessos não recarregam

Reflect: A API Complementar

Entendendo Reflect

Reflect é um objeto built-in que fornece métodos estáticos para operações interceptáveis. Seu principal benefício é consistência e segurança: enquanto obj.prop pode falhar silenciosamente, Reflect.get() retorna valores previsíveis. A combinação Proxy + Reflect é o padrão profissional.

const pessoa = { nome: 'Carol', idade: 28 };

const handler = {
  get(target, prop) {
    // Usar Reflect garante o mesmo comportamento do JavaScript
    if (prop === 'idade') {
      return Reflect.get(target, prop) + 1; // Retorna próxima idade
    }
    return Reflect.get(target, prop);
  },
  set(target, prop, value) {
    console.log(`Mudança rastreada: ${prop}`);
    return Reflect.set(target, prop, value);
  }
};

const pessoaProxy = new Proxy(pessoa, handler);
console.log(pessoaProxy.idade); // 29
pessoaProxy.nome = 'David'; // "Mudança rastreada: nome"

Por Que Não Apenas Usar target[prop]?

Reflect trata edge cases corretamente: propriedades não-configuráveis, descriptores personalizados e prototype chains. Em times ágeis, isso significa menos bugs em refatorações. Reflect também funciona com símbolos, o que in e delete não fazem uniformemente.

const simbolo = Symbol('privado');
const obj = {};
Object.defineProperty(obj, simbolo, {
  value: 'secreto',
  configurable: false
});

const proxy = new Proxy(obj, {
  get(target, prop) {
    // Reflect respeita propriedades não-configuráveis
    return Reflect.get(target, prop);
  }
});

console.log(proxy[simbolo]); // "secreto"

Padrões Avançados para Times Ágeis

Validação Reutilizável

Ao invés de espalhá-la pela codebase, centralize em um factory de validadores:

function criarProxyValidado(target, esquema) {
  return new Proxy(target, {
    set(target, prop, value) {
      const validador = esquema[prop];
      if (validador && !validador(value)) {
        throw new TypeError(`Validação falhou para ${prop}`);
      }
      return Reflect.set(target, prop, value);
    }
  });
}

const schemaPedido = {
  quantidade: (v) => Number.isInteger(v) && v > 0,
  preco: (v) => typeof v === 'number' && v >= 0
};

const pedido = criarProxyValidado({}, schemaPedido);
pedido.quantidade = 5; // ✓
pedido.preco = -10;    // Error: Validação falhou para preco

Observable Pattern com Reflect

Implemente um sistema reativo simples para notificar subscribers de mudanças:

function criarObservable(target, onChange) {
  return new Proxy(target, {
    set(target, prop, value) {
      const anterior = Reflect.get(target, prop);
      const sucesso = Reflect.set(target, prop, value);
      if (sucesso && anterior !== value) {
        onChange({ prop, anterior, novo: value });
      }
      return sucesso;
    }
  });
}

const estado = criarObservable({ contador: 0 }, (mudanca) => {
  console.log(`${mudanca.prop}: ${mudanca.anterior} → ${mudanca.novo}`);
});

estado.contador = 1; // "contador: 0 → 1"
estado.contador = 2; // "contador: 1 → 2"

Conclusão

Proxy e Reflect são pilares de código escalável e manutenível em JavaScript. Aprendemos que Proxies interceptam operações para validação, logging e lazy loading; Reflect fornece a API correta para acessar essas operações de forma segura e previsível. Em times ágeis, esses padrões reduzem duplicação, centralizam regras e facilitam refatorações sem quebrar a codebase. Comece pequeno—valide um formulário ou implemente um observable—e evolua gradualmente.

Referências


Artigos relacionados