Implementação e Uso de Decorators no JavaScript Moderno

Os Decorators representam uma proposta de sintaxe para o JavaScript (ES6+) que permite anotar e modificar o comportamento de classes e seus membros (métodos e propriedades) de maneira declarativa. Na prática, um decorator nada mais é do que uma função pura que recebe o alvo da modificação como argumento e retorna esse alvo modificado ou um novo descritor.

Escopo de Aplicação

Diferente de outras funcionalidades, os decorators possuem restrições sobre onde podem ser aplicados:

  • Classes: Podem modificar a definição global da classe.
  • Membros de Classe: Incluindo métodos, propriedades, acessoers (get/set).
  • Funções Simples: Não podem ser decoradas devido ao hoisting (içamento) de funções no JavaScript.

Decorators de Classe

Quando aplicado a uma classe, o decorator recebe como único argumento o construtor da classe que está sendo decorada.

function registrarData(target) {
  target.criadoEm = new Date().toISOString();
}

@registrarData
class Usuario {
  // Lógica da classe
}

console.log(Usuario.criadoEm); // Exibe a data de criação vinculada à classe

Caso seja necessário passar parâmetros para o decorator, utiliza-se o padrão Factory, onde uma função externa envolve a lógica do decorator e retorna a função que será executada pelo motor do JavaScript.

function definirVersao(identificador) {
  return function(construtor) {
    construtor.prototype.versaoApp = identificador;
  };
}

@definirVersao('2.1.0')
class Aplicacao {
  executar() {
    console.log(`Rodando versão ${this.versaoApp}`);
  }
}

const app = new Aplicacao();
app.executar(); // "Rodando versão 2.1.0"

Note que as alterações realizadas por um decorator ocorrem no momento da compilação (ou transpilação), e não em tempo de execução para cada instância, o que torna o processo altamente performático.

Decorators de Métodos

A decoração de métodos permite interceptar chamadas, modificar permissões ou realizar logs. Neste caso, a função recebe três parâmetros:

  1. target: O protótipo da classe.
  2. key: O nome do membro que está sendo decorado.
  3. descriptor: O objeto de descrição da propriedade (contendo configurações como writable, enumerable, etc).
function bloqueado(target, nome, descriptor) {
  // Impede que o método seja sobrescrito
  descriptor.writable = false;
  return descriptor;
}

class Servico {
  @bloqueado
  conectar() {
    console.log("Conexão estabelecida.");
  }
}

const srv = new Servico();
// Tentativa de alterar o método resultará em erro ou será ignorada
srv.conectar = () => console.log("Hackeado!");
srv.conectar(); // "Conexão estabelecida."

Interceptação de Lógica

Um uso comum é envolver a lógica original do método para adicionar funcionalidades transversais, como telemetria ou validação.

function monitorarDesempenho(target, chave, descritor) {
  const metodoOriginal = descritor.value;

  descritor.value = function(...args) {
    console.time(chave);
    const retorno = metodoOriginal.apply(this, args);
    console.timeEnd(chave);
    return retorno;
  };

  return descritor;
}

class Processador {
  @monitorarDesempenho
  executarTarefaComplexa() {
    // Simulação de processamento
    for(let i = 0; i < 1000000; i++) {}
    return "Finalizado";
  }
}

const proc = new Processador();
proc.executarTarefaComplexa(); // Exibe o tempo gasto no console

Os decorators facilitam a separação de preocupações (Separation of Conecrns), permitindo que regras de negócio permaneçam limpas enquanto comportamentos genéricos são abstraídos para funções utilitárias reutilizáveis.

Tags: javascript ES6 decorators web-development OOP

Publicado em 6-17 21:00