JavaScript

Como Usar Symbols, WeakMap e WeakSet em JavaScript em Produção

7 min de leitura

Como Usar Symbols, WeakMap e WeakSet em JavaScript em Produção

Symbols: Criando Identificadores Únicos Symbols são primitivos únicos em JavaScript que servem como identificadores imutáveis. Diferentemente de strings, dois symbols nunca serão iguais, mesmo que criados com a mesma descrição. Isso os torna ideais para criar chaves privadas em objetos, evitando colisões de nomes e protegendo dados sensíveis de acesso acidental. Um caso de uso real é criar propriedades privadas em classes sem expor internals. Symbols não aparecem em iterações com , ou , tornando-os perfeitos para dados que devem existir mas não serem enumeráveis. Use quando precisar de symbols compartilhadas entre contextos (como iframes ou workers). WeakMap: Mapeamentos com Referências Fracas WeakMap é um tipo de mapa que mantém referências fracas às suas chaves. Isso significa que se a chave for deletada da memória, a entrada correspondente será automaticamente removida do WeakMap. Diferentemente de Map comum, WeakMap só aceita objetos como chaves e não é iterável. Na produção, WeakMap é excelente para associar dados privados a DOM nodes ou

<h2>Symbols: Criando Identificadores Únicos</h2>

<p>Symbols são primitivos únicos em JavaScript que servem como identificadores imutáveis. Diferentemente de strings, dois symbols nunca serão iguais, mesmo que criados com a mesma descrição. Isso os torna ideais para criar chaves privadas em objetos, evitando colisões de nomes e protegendo dados sensíveis de acesso acidental.</p>

<pre><code class="language-javascript">// Criando symbols

const idPrivado = Symbol(&#039;id&#039;);

const permissao = Symbol(&#039;admin&#039;);

const usuario = {

nome: &#039;João&#039;,

[idPrivado]: 12345,

[permissao]: false

};

console.log(usuario.nome); // &#039;João&#039;

console.log(usuario[idPrivado]); // 12345

console.log(Object.keys(usuario)); // [&#039;nome&#039;] - symbols não aparecem!</code></pre>

<p>Um caso de uso real é criar propriedades privadas em classes sem expor internals. Symbols não aparecem em iterações com <code>for...in</code>, <code>Object.keys()</code> ou <code>JSON.stringify()</code>, tornando-os perfeitos para dados que devem existir mas não serem enumeráveis. Use <code>Symbol.for()</code> quando precisar de symbols compartilhadas entre contextos (como iframes ou workers).</p>

<pre><code class="language-javascript">// Exemplo em produção: classe com dados privados

class ContaBancaria {

#saldo = 0; // Alternativa moderna (private fields)

constructor(titular, saldoInicial) {

this.titular = titular;

this.#saldo = saldoInicial;

}

depositar(valor) {

this.#saldo += valor;

}

obterSaldo() {

return this.#saldo;

}

}

const conta = new ContaBancaria(&#039;Maria&#039;, 1000);

conta.depositar(500);

console.log(conta.obterSaldo()); // 1500

console.log(conta.saldo); // undefined - protegido!</code></pre>

<h2>WeakMap: Mapeamentos com Referências Fracas</h2>

<p>WeakMap é um tipo de mapa que mantém referências fracas às suas chaves. Isso significa que se a chave for deletada da memória, a entrada correspondente será automaticamente removida do WeakMap. Diferentemente de Map comum, WeakMap só aceita objetos como chaves e não é iterável.</p>

<pre><code class="language-javascript">// WeakMap na prática

const cachePrivado = new WeakMap();

class Usuario {

constructor(nome) {

this.nome = nome;

}

obterMetadados() {

if (!cachePrivado.has(this)) {

cachePrivado.set(this, {

criadoEm: new Date(),

acessoTotal: 0

});

}

const meta = cachePrivado.get(this);

meta.acessoTotal++;

return meta;

}

}

let usuario1 = new Usuario(&#039;Pedro&#039;);

console.log(usuario1.obterMetadados()); // { criadoEm: ..., acessoTotal: 1 }

console.log(usuario1.obterMetadados()); // { criadoEm: ..., acessoTotal: 2 }

usuario1 = null; // Quando deletado, WeakMap libera automaticamente</code></pre>

<p>Na produção, WeakMap é excelente para associar dados privados a DOM nodes ou instâncias de classe. Evita memory leaks porque os dados são automaticamente coletados quando o objeto é deletado. Use-a para caching de metadados, eventos privados ou informações vinculadas a objetos específicos que podem ser destruídos.</p>

<pre><code class="language-javascript">// Exemplo real: rastrear listeners de eventos

const listeningMap = new WeakMap();

function adicionarListener(elemento, evento, callback) {

if (!listeningMap.has(elemento)) {

listeningMap.set(elemento, {});

}

const listeners = listeningMap.get(elemento);

if (!listeners[evento]) {

listeners[evento] = [];

}

listeners[evento].push(callback);

elemento.addEventListener(evento, callback);

}

function removerElement(elemento) {

const listeners = listeningMap.get(elemento);

if (listeners) {

Object.entries(listeners).forEach(([evento, callbacks]) =&gt; {

callbacks.forEach(cb =&gt; elemento.removeEventListener(evento, cb));

});

}

elemento.remove();

// WeakMap automaticamente limpa a entrada

}</code></pre>

<h2>WeakSet: Conjuntos com Referências Fracas</h2>

<p>WeakSet funciona como Set, mas mantém referências fracas aos seus elementos. Ideal para rastrear objetos sem impedir sua coleta de lixo. Assim como WeakMap, só aceita objetos, não é iterável e oferece apenas métodos <code>.add()</code>, <code>.has()</code> e <code>.delete()</code>.</p>

<pre><code class="language-javascript">// WeakSet para rastrear objetos autenticados

const usuariosAutenticados = new WeakSet();

class Sessao {

constructor(usuario) {

this.usuario = usuario;

usuariosAutenticados.add(usuario);

}

verificarAutenticacao(usuario) {

return usuariosAutenticados.has(usuario);

}

}

const user = { id: 1, nome: &#039;Ana&#039; };

const sessao = new Sessao(user);

console.log(sessao.verificarAutenticacao(user)); // true</code></pre>

<h3>Comparação Prática: WeakSet vs Set</h3>

<p>WeakSet evita memory leaks ao não impedir coleta de lixo, enquanto Set mantém referências fortes. Use WeakSet quando precisar apenas verificar &quot;se um objeto foi registrado&quot;, sem necessidade de iteração. Um exemplo real é rastrear instâncias já processadas em um ciclo de vida.</p>

<pre><code class="language-javascript">// Padrão: evitar reprocessamento de objetos

const processados = new WeakSet();

function processarObjeto(obj) {

if (processados.has(obj)) {

console.log(&#039;Já foi processado&#039;);

return;

}

console.log(&#039;Processando:&#039;, obj);

// Lógica pesada aqui...

processados.add(obj);

}

let objeto = { id: 1 };

processarObjeto(objeto); // Processando: { id: 1 }

processarObjeto(objeto); // Já foi processado

objeto = null; // Liberado da memória automaticamente</code></pre>

<h2>Boas Práticas em Produção</h2>

<p>Combine esses três recursos estrategicamente. Use <strong>Symbols</strong> para evitar colisões de chaves em bibliotecas compartilhadas ou quando trabalhar com plugins de terceiros. Use <strong>WeakMap</strong> para armazenar dados privados associados a instâncias, especialmente em frameworks como React (por exemplo, para rastrear componentes). Use <strong>WeakSet</strong> para rastrear membros de um grupo sem afetar garbage collection.</p>

<p>Evite criar aplicações complexas que dependem de iteração sobre WeakMap/WeakSet — se precisar iterar, use Map/Set normais. Não confunda Symbols com strings para segurança: symbols oferecem proteção contra acesso acidental, não contra ataques deliberados. Em produção, sempre teste memory leaks com ferramentas como Chrome DevTools para validar que WeakMap e WeakSet estão funcionando corretamente.</p>

<h2>Conclusão</h2>

<p>Symbols, WeakMap e WeakSet são ferramentas poderosas para escrever código robusto e eficiente. <strong>Symbols</strong> criam identificadores únicos e imutáveis, protegendo propriedades de objetos sem aparecer em enumerações. <strong>WeakMap</strong> associa dados a objetos sem impedir coleta de lixo, ideal para caching e dados privados ligados ao ciclo de vida. <strong>WeakSet</strong> rastreia objeto sem manter referências fortes, prevenindo memory leaks em padrões de validação e processamento.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol" target="_blank" rel="noopener noreferrer">MDN - Symbol</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap" target="_blank" rel="noopener noreferrer">MDN - WeakMap</a></li>

<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet" target="_blank" rel="noopener noreferrer">MDN - WeakSet</a></li>

<li><a href="https://javascript.info/weakmap-weakset" target="_blank" rel="noopener noreferrer">JavaScript.info - WeakMap and WeakSet</a></li>

<li><a href="https://github.com/getify/You-Dont-Know-JS" target="_blank" rel="noopener noreferrer">You Don&#039;t Know JS - ES6 &amp; Beyond</a></li>

</ul>

Comentários

Mais em JavaScript

Migração de JavaScript para TypeScript em Projetos Existentes: Do Básico ao Avançado
Migração de JavaScript para TypeScript em Projetos Existentes: Do Básico ao Avançado

Preparação e Diagnóstico do Projeto Antes de iniciar qualquer migração, você...

Guia Completo de Testes End-to-End em JavaScript com Playwright e Cypress
Guia Completo de Testes End-to-End em JavaScript com Playwright e Cypress

Introdução aos Testes End-to-End Testes end-to-end (E2E) verificam sua aplica...

Boas Práticas de Variáveis de Ambiente e Configuração em Projetos JavaScript para Times Ágeis
Boas Práticas de Variáveis de Ambiente e Configuração em Projetos JavaScript para Times Ágeis

Introdução: Por Que Variáveis de Ambiente Importam Variáveis de ambiente são...