É possível criar uma “referência fraca” em javascript?

97

Existe alguma maneira em javascript de criar uma "referência fraca" para outro objeto? Aqui está a página wiki que descreve o que é uma referência fraca. Aqui está outro artigo que os descreve em Java. Alguém pode pensar em uma maneira de implementar esse comportamento em javascript?

Stephen Cagle
fonte
4
Referências fracas estão sendo discutidas para ES6. Mantenha seus olhos abertos.
Ryan Smith
2
* Especificação oficial wiki / discussão em wiki.ecmascript.org/doku.php?id=strawman:weak_refs , atualmente “Última modificação: 02/02/2013 22:25” * alguma outra discussão de especificação em esdiscuss.org/topic/what -é-o-status-de-referências-fracas , última postagem no momento “Dom 3 de março 11h56:05 PST 2013”
Destiny Architect,
Na maioria dos casos, os WRs são uma tentativa de resolver o problema do ouvinte prescrito , discutido aqui: [ stackoverflow.com/questions/43758217/… . Se essa pergunta tivesse uma boa resposta, não acho que haveria muita necessidade de WRs.
James
@supercat Eu postei uma resposta para a pergunta do ouvinte expirado .
James

Respostas:

39

Não há suporte de linguagem para referências fracas em JavaScript. Você pode fazer o seu próprio usando a contagem de referência manual, mas não de maneira especialmente suave. Você não pode fazer um objeto proxy wrapper, porque em JavaScript os objetos nunca sabem quando estão prestes a ser coletados como lixo.

Assim, sua 'referência fraca' se torna uma chave (por exemplo, inteiro) em uma pesquisa simples, com um método de adicionar referência e remover referência, e quando não há mais referências manualmente rastreadas, a entrada pode ser excluída, deixando pesquisas futuras em essa chave para retornar nulo.

Este não é realmente um fraco ref, mas pode resolver alguns dos mesmos problemas. Normalmente é feito em aplicativos da web complexos para evitar vazamento de memória de navegadores (normalmente IE, especialmente em versões mais antigas) quando há um loop de referência entre um nó DOM ou manipulador de eventos e um objeto associado a ele, como um encerramento. Nestes casos, um esquema completo de contagem de referência pode nem mesmo ser necessário.

bobince
fonte
2
Não examinei (ou usei) cuidadosamente o código, mas es-lab tem um script que fornece emulação básica de WeakMap . Aurora 6 (Mozilla) tem uma implementação WeakMap não padrão .
theazureshadow
2
Com ES6 esta resposta não é mais correta. Veja minha resposta abaixo stackoverflow.com/a/28567560/745190
thelastshadow
9
Ainda está correto, porque ES6 WeakMaps não são verdadeiras referências fracas. WeakMaps aceitam objetos apenas como chaves, e as referências a esses objetos são mantidas fracamente. Consulte stackoverflow.com/questions/32397729/…
CodeManX
Eu escrevi uma aula para emular um mapa fraco e postei aqui: stackoverflow.com/a/47017206/491553
Ryan Shillington
11

Atualização: setembro de 2019

Não é possível usar referências fracas ainda, mas provavelmente em breve será possível, pois WeakRefs em JavaScript são Work In Progress. Detalhes abaixo.

Proposta

A proposta está agora no Estágio 3, o que significa que possui especificações completas e que mais refinamento exigirá feedback das implementações e dos usuários.

A proposta WeakRef engloba duas novas peças principais de funcionalidade:

  • Criação de referências fracas para objetos com a classe WeakRef
  • Executar finalizadores definidos pelo usuário após a coleta de lixo dos objetos, com a classe FinalizationGroup

Casos de uso

Um uso principal para referências fracas é implementar caches ou mapeamentos contendo objetos grandes, onde é desejável que um objeto grande não seja mantido vivo apenas porque aparece em um cache ou mapeamento.

A finalização é a execução de código para limpar após um objeto que se tornou inacessível para execução do programa. Os finalizadores definidos pelo usuário permitem vários novos casos de uso e podem ajudar a evitar vazamentos de memória ao gerenciar recursos que o coletor de lixo não conhece.

Fonte e outras leituras

https://github.com/tc39/proposal-weakrefs
https://v8.dev/features/weak-references

M. Twarog
fonte
1
O Firefox Nightly adicionou suporte experimental para WeakRef. Aqui está um exemplo de implementação usando-o para criar uma versão iterável do WeakSet: gist.github.com/seanlinsley/bc10378fd311d75cf6b5e80394be813d
seanlinsley
3

Referências realmente fracas, não, ainda não (mas os fabricantes de navegadores estão olhando para o assunto). Mas aqui está uma ideia de como simular referências fracas.

Você pode construir um cache através do qual você dirige seus objetos. Quando um objeto é armazenado, o cache mantém uma previsão de quanta memória o objeto ocupará. Para alguns itens, como armazenamento de imagens, isso é fácil de resolver. Para outros, isso seria mais difícil.

Quando você precisa de um objeto, solicita-o ao cache. Se o cache tiver o objeto, ele será retornado. Se não estiver lá, o item será gerado, armazenado e devolvido.

As referências fracas são simuladas pela remoção de itens de cache, quando a quantidade total de memória prevista atinge um determinado nível. Ele vai prever quais itens são menos usados ​​com base na frequência com que são recuperados, ponderada por quanto tempo atrás foram retirados. Um custo de 'cálculo' também pode ser adicionado, se o código que cria o item for passado para o cache como um fechamento. Isso permitiria que o cache mantenha itens que são muito caros para construir ou gerar.

O algoritmo de exclusão é fundamental, porque se você errar, poderá acabar removendo os itens mais populares. Isso causaria um desempenho terrível.

Contanto que o cache seja o único objeto com referências permanentes aos objetos armazenados, o sistema acima deve funcionar muito bem como uma alternativa às verdadeiras referências fracas.

JL235
fonte
25
A maior parte do que você disse não é irrelevante para os fracos refs?
Erik Kaplun
22
@ JL235 - o uso importante para referências fracas não é para caches, mas para manipuladores de eventos. Tenho algum objeto que, enquanto existir, deveria observar algum outro evento - mas não quero que o fato de estar em uma lista de notificações constitua uma referência para os propósitos do CG.
Malvolio
7
Referências fracas não têm nada a ver com cache. Uma referência fraca significa que você deseja rastrear algo, mas se não houver referências restantes ao objeto sendo rastreado, você permite que ele seja excluído.
fabspro
8
Há claramente um caso de uso para construir um cache usando referências fracas para expiração automática.
Phil Freeman
5
O cache é tradicionalmente o principal motivo para referências fracas. O manipulador de eventos DOM é apenas uma coisa com erros do explorador do IE.
axkibe
2

Usar um mecanismo de cache para emular uma referência fraca, como JL235 sugerido acima , é razoável. Se referências fracas existissem nativamente, você observaria um comportamento como este:

this.val = {};
this.ref = new WeakReference(this.val);
...
this.ref.get(); // always returns val
...
this.val = null; // no more references
...
this.ref.get(); // may still return val, depending on already gc'd or not

Já com um cache, você observaria:

this.val = {};
this.key = cache.put(this.val);
...
cache.get(this.key); // returns val, until evicted by other cache puts
...
this.val = null; // no more references
...
cache.get(this.key); // returns val, until evicted by other cache puts

Como detentor de uma referência, você não deve fazer suposições sobre quando se refere a um valor, isso não é diferente usando um cache

Markus
fonte
-4

EcmaScript 6 (ES Harmony) tem um objeto WeakMap . O suporte do navegador entre os navegadores modernos é muito bom (as últimas 3 versões do Firefox, Chrome e até uma próxima versão do IE o suportam).

a sombra da sombra
fonte
29
Isso não é exatamente o mesmo. A WeakMapnão fornece referências fracas para objetos - não são os valores que são referências fracas no WeakMap, mas as chaves . O fato de existirem referências fracas no mapa é apenas um mecanismo de prevenção de vazamento de memória e, de outra forma, não pode ser observado pelo usuário.
EyasSH
1
Você está correto ao dizer que são as chaves que são fracas, não os valores. Mas todo o propósito de usar referências fracas é permitir a coleta de lixo do objeto referenciado. O OP postou dois links, o segundo dos quais é sobre como adicionar um id a um objeto que você não pode estender e, na verdade, ele recomenda o uso de WeakHashMap, o equivalente em Java do WeakMap do JavaScript.
sombra de
12
boa sorte usando WeakMap para implementar uma referência fraca desde weakmap.get(new String('any possible key that has ever existed or ever will exist'))irá sempre ser undefined. Não é útil. Votação negativa!
user3338098
-5

http://www.jibbering.com/faq/faq_notes/closures.html

ECMAScript usa coleta de lixo automática. A especificação não define os detalhes, deixando isso para os implementadores resolverem, e algumas implementações são conhecidas por dar uma prioridade muito baixa às suas operações de coleta de lixo. Mas a ideia geral é que se um objeto se tornar não referenciável (por não ter referências remanescentes a ele deixadas acessíveis para a execução do código), ele se torna disponível para coleta de lixo e em algum ponto futuro será destruído e quaisquer recursos que esteja consumindo serão liberados e retornados ao sistema para reutilização.

Isso normalmente seria o caso ao sair de um contexto de execução. A estrutura da cadeia de escopo, o objeto Ativação / Variável e quaisquer objetos criados dentro do contexto de execução, incluindo objetos de função, não seriam mais acessíveis e, portanto, ficariam disponíveis para a coleta de lixo.

O que significa que não há fracos, apenas aqueles que não estão mais disponíveis.

Branchgabriel
fonte
10
Evitar ciclos de referência não é a única razão para usar referências fracas. Eles são muito úteis para pool de instância de objeto / cache e assim por diante.
fofo
A definição de WeakReference não é uma questão. Também concordo com o comentário acima.
Yuri Yaryshev