Desafio JavaScript
Cache com WeakMap e contagem de acessos
Enunciado
Implemente uma função criarCache() que retorna um objeto com dois métodos: obter(chave, calcular) e estatisticas(). O método obter recebe um objeto chave e uma função calcular. Se o resultado para aquela chave ainda não estiver no cache, ele chama calcular(chave), armazena o resultado em um WeakMap e retorna. Se já estiver, retorna o valor armazenado. O método estatisticas retorna um Map com a contagem de quantas vezes cada chave foi acessada (incluindo acertos e erros de cache).
Requisitos
- Use
WeakMappara armazenar os resultados do cache. - Use
Mappara armazenar as contagens de acesso por chave. - O método
obterdeve chamarcalcularapenas na primeira vez para cada chave. - O método
estatisticasdeve retornar umMapcom as contagens. - As chaves são objetos e não devem vazar memória (WeakMap).
Código inicial
function criarCache() {
// seu código aqui
}
// Exemplo de uso:
const cache = criarCache();
const obj1 = {};
const obj2 = {};
console.log(cache.obter(obj1, () => 'valor1')); // 'valor1'
console.log(cache.obter(obj1, () => 'valor1')); // 'valor1' (do cache)
console.log(cache.obter(obj2, () => 'valor2')); // 'valor2'
console.log(cache.estatisticas()); // Map(2) { {} => 2, {} => 1 }
Saída esperada
valor1
valor1
valor2
Map(2) { {} => 2, {} => 1 }
Ver dica
No obter, primeiro incremente a contagem no Map de estatísticas. Depois verifique se o WeakMap já tem a chave com has. Se não tiver, chame calcular(chave) e armazene com set.
Mostrar solução
function criarCache() {
const cache = new WeakMap(); // armazena resultados
const acessos = new Map(); // armazena contagens por chave
return {
obter(chave, calcular) {
// Atualiza a contagem de acessos
acessos.set(chave, (acessos.get(chave) || 0) + 1);
// Se já está no cache, retorna o valor
if (cache.has(chave)) {
return cache.get(chave);
}
// Senão, calcula, armazena e retorna
const valor = calcular(chave);
cache.set(chave, valor);
return valor;
},
estatisticas() {
return new Map(acessos); // cópia para evitar mutações externas
}
};
}
// Exemplo de uso
const cache = criarCache();
const obj1 = {};
const obj2 = {};
console.log(cache.obter(obj1, () => 'valor1')); // 'valor1'
console.log(cache.obter(obj1, () => 'valor1')); // 'valor1' (do cache)
console.log(cache.obter(obj2, () => 'valor2')); // 'valor2'
console.log(cache.estatisticas()); // Map(2) { {} => 2, {} => 1 }
Passo a passo
- Dentro de
criarCache, criamos umWeakMapchamadocachepara armazenar os resultados associados a objetos chave. - Criamos um
Mapchamadoacessospara contar quantas vezes cada chave foi acessada. - Retornamos um objeto com dois métodos:
obtereestatisticas. - No método
obter, primeiro incrementamos a contagem emacessosusandosetegetcom fallback|| 0. - Verificamos se a chave já está no
cachecomcache.has(chave). Se sim, retornamoscache.get(chave). - Se não estiver, chamamos
calcular(chave), armazenamos o resultado nocachecomcache.set(chave, valor)e retornamos o valor. - O método
estatisticasretorna uma cópia doMapde acessos para evitar que o chamador modifique o estado interno.
Por que funciona
O WeakMap é perfeito para cache porque não impede que as chaves sejam coletadas pelo garbage collector quando não forem mais referenciadas em outro lugar. Já o Map de acessos mantém uma referência forte às chaves, mas como é interno e controlado, não causa vazamento se as chaves forem removidas do uso externo? Na verdade, o Map de acessos mantém referências fortes, então as chaves não serão coletadas enquanto o cache existir. Para um cenário ideal de WeakMap, as estatísticas também deveriam usar WeakMap, mas como precisamos iterar para retornar um Map, usamos Map e assumimos que o número de chaves é gerenciado. O conceito principal é demonstrar o uso de WeakMap para cache e Map para contagem.
Erros comuns
- Usar
cache.get(chave)sem verificarhas: se a chave não existir,getretornaundefined, e você pode acabar chamandocalculardesnecessariamente ou retornandoundefinedcomo se fosse um valor válido. - Tentar iterar sobre o
WeakMap:WeakMapnão é iterável; se você tentarfor...ofou[...cache], dará erro. UseMappara estatísticas. - Esquecer de incrementar a contagem antes de verificar o cache: a contagem deve incluir todos os acessos, tanto acertos quanto erros. Se incrementar só no erro, as estatísticas ficam erradas.
- Retornar o
Mapinterno diretamente: isso permite que o código externo modifique as contagens. Sempre retorne uma cópia (new Map(acessos)).
Outra forma de resolver
Se as chaves pudessem ser strings ou números, poderíamos usar um Map comum para o cache. Porém, para objetos, o WeakMap evita vazamentos. Uma alternativa é usar Map e limpar manualmente, mas isso é propenso a erros.
Saída esperada
valor1
valor1
valor2
Map(2) { {} => 2, {} => 1 }