<h2>Object.defineProperty: Controle Fino sobre Propriedades</h2>
<p>Object.defineProperty é o alicerce da metaprogramação em JavaScript. Ele permite definir propriedades de objetos com controle granular sobre comportamentos como leitura, escrita e enumeração. Diferentemente da atribuição simples, este método possibilita criar getters, setters e descriptores que interceptam operações.</p>
<pre><code class="language-javascript">const usuario = {};
Object.defineProperty(usuario, 'nome', {
value: 'João',
writable: false, // impede alteração
enumerable: true, // aparece em for...in
configurable: false // impede redefinição
});
usuario.nome; // 'João'
usuario.nome = 'Maria'; // silencioso em modo não-strict
console.log(usuario.nome); // 'João'
// Com getter e setter
Object.defineProperty(usuario, 'idade', {
get() {
return this._idade || 0;
},
set(value) {
if (value < 0) throw new Error('Idade inválida');
this._idade = value;
},
enumerable: true,
configurable: true
});
usuario.idade = 25; // ativa o setter
console.log(usuario.idade); // 25</code></pre>
<p>Esse controle é fundamental para validação, reatividade e proteção de dados. Frameworks como Vue.js usam getters/setters para detectar mudanças e atualizar a interface automaticamente.</p>
<h2>Validação e Proteção de Dados com Descriptores</h2>
<p>Combinando Object.defineProperty com lógica de negócio, criamos camadas de proteção robustas. Um caso prático é forçar validação antes de armazenar valores ou permitir apenas leitura de certas propriedades.</p>
<pre><code class="language-javascript">class Produto {
constructor(nome, preco) {
this._preco = preco;
Object.defineProperty(this, 'preco', {
get() {
return this._preco;
},
set(valor) {
if (typeof valor !== 'number' || valor < 0) {
throw new TypeError('Preço deve ser um número positivo');
}
console.log(Preço atualizado: R$ ${valor});
this._preco = valor;
},
enumerable: true
});
Object.defineProperty(this, 'nome', {
value: nome,
writable: false,
enumerable: true
});
}
}
const produto = new Produto('Notebook', 2500);
produto.preco = 2800; // Preço atualizado: R$ 2800
produto.preco = -100; // TypeError: Preço deve ser um número positivo
produto.nome = 'Desktop'; // silencioso, não altera</code></pre>
<p>Essa abordagem é essencial em aplicações financeiras, sistemas de permissões e validação de dados em tempo real.</p>
<h2>Decorators: Sintaxe Moderna para Metaprogramação</h2>
<p>Decorators são uma sintaxe elegante (ainda experimental em JavaScript, mas já padrão em TypeScript) que encapsula a lógica de Object.defineProperty e reflection. Um decorator é uma função que modifica o comportamento de classes, métodos ou propriedades.</p>
<pre><code class="language-javascript">// Decorator simples para logging
function log(target, propertyKey, descriptor) {
const metodoOriginal = descriptor.value;
descriptor.value = function(...args) {
console.log(Chamando ${propertyKey} com args:, args);
const resultado = metodoOriginal.apply(this, args);
console.log(${propertyKey} retornou:, resultado);
return resultado;
};
return descriptor;
}
class Calculadora {
@log
somar(a, b) {
return a + b;
}
}
const calc = new Calculadora();
calc.somar(5, 3);
// Chamando somar com args: [5, 3]
// somar retornou: 8</code></pre>
<p>Para usar decorators no JavaScript padrão (sem TypeScript), você precisará de um transpilador. Mas a ideia é poderosa: separar a lógica de negócio da infraestrutura.</p>
<h3>Decorators Reutilizáveis</h3>
<pre><code class="language-javascript">// Decorator para cachear resultados
function cache(target, propertyKey, descriptor) {
const metodo = descriptor.value;
const cache = new Map();
descriptor.value = function(...args) {
const chave = JSON.stringify(args);
if (cache.has(chave)) {
console.log('Retornando do cache');
return cache.get(chave);
}
const resultado = metodo.apply(this, args);
cache.set(chave, resultado);
return resultado;
};
return descriptor;
}
class Fibonacci {
@cache
calcular(n) {
console.log(Calculando fib(${n}));
if (n <= 1) return n;
return this.calcular(n - 1) + this.calcular(n - 2);
}
}
const fib = new Fibonacci();
fib.calcular(5);
fib.calcular(5); // Retornando do cache</code></pre>
<p>Decorators são particularmente úteis em autenticação, validação, auditoria e transformação de dados de forma declarativa.</p>
<h2>Reflection e Introspeção Avançada</h2>
<p>A verdadeira potência da metaprogramação emerge quando combinamos Object.defineProperty com Reflect API para introspecção. Isso permite inspecionar e modificar estruturas em tempo de execução com segurança.</p>
<pre><code class="language-javascript">class Usuario {
constructor(dados) {
// Cria propriedades reativas
Object.keys(dados).forEach(chave => {
Object.defineProperty(this, chave, {
get() {
return this[_${chave}];
},
set(valor) {
if (Reflect.has(this, _${chave})) {
console.log(Campo ${chave} alterado para ${valor});
}
this[_${chave}] = valor;
},
enumerable: true
});
});
// Popula valores iniciais
Object.assign(this, dados);
}
toJSON() {
return Object.getOwnPropertyNames(this)
.filter(p => !p.startsWith('_'))
.reduce((obj, chave) => {
obj[chave] = this[chave];
return obj;
}, {});
}
}
const user = new Usuario({ email: 'teste@mail.com', ativo: true });
user.email = 'novo@mail.com'; // Campo email alterado para novo@mail.com
console.log(JSON.stringify(user)); // {"email":"novo@mail.com","ativo":true}</code></pre>
<p>Este padrão é fundamental em frameworks modernos para reatividade automática e serialização inteligente de dados.</p>
<h2>Conclusão</h2>
<p>Metaprogramação em JavaScript, através de Object.defineProperty e Decorators, permite criar código mais elegante, seguro e manutenível. <strong>Primeiro</strong>, Object.defineProperty fornece controle preciso sobre propriedades, essencial para validação e proteção de dados. <strong>Segundo</strong>, Decorators oferecem uma sintaxe limpa e reutilizável para separar concerns de infraestrutura da lógica de negócio. <strong>Terceiro</strong>, combinados com Reflect, esses mecanismos possibilitam frameworks reativos e sistemas de auditoria sofisticados—compreendê-los é fundamental para dominar JavaScript moderno.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty" target="_blank" rel="noopener noreferrer">MDN - Object.defineProperty</a></li>
<li><a href="https://github.com/tc39/proposal-decorators" target="_blank" rel="noopener noreferrer">MDN - Decorators Proposal</a></li>
<li><a href="https://javascript.info/property-descriptors" target="_blank" rel="noopener noreferrer">JavaScript.info - Property flags and descriptors</a></li>
<li><a href="https://tc39.es/ecma262/#sec-reflect" target="_blank" rel="noopener noreferrer">TC39 - Reflect API Specification</a></li>
<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="noopener noreferrer">You Don't Know JS - Types & Grammar (Kyle Simpson)</a></li>
</ul>