Strong & Weak References in JavaScript

Donnerstag, 28.9.2023

Es gibt einen Unterschied zwischen Strong und Weak References in JavaScript.

Kennen Sie den Unterschied zwischen Strong und Weak References? Dieser Beitrag legt die Unterschiede dar und zeigt Einsatzszenarien auf.  

Ich muss ehrlich gesagt gestehen, dass ich im Code von Bibliotheken ab und zu mal über die WeakMap stolpere, mich aber nie gefragt habe, was diese Datenstruktur so besonders macht: Also habe ich an einem Freitag mal daran gemacht zu recherchieren. 

 

Strong References 

Zu Beginn möchte ich mit einem Codebeispiel starten: 

Was ist in diesem Codebeispiel die Ausgabe von Zeile 7?  

Die Antwort ist ein Array, gefüllt mit einem Objekt, mit einem key name, und dem Wert Christian. Wieso ist dies so? Immerhin haben wir die Variable person, welche in unserem Array steckt, doch auf undefined gesetzt? Auch wenn man das im ersten Moment denken mag, so haben wir in Wahrheit doch nur die Referenz unserer Variable auf den Wert gelöscht. Unser Array referenziert weiterhin unser Objekt und so bleibt dieses im Speicher.  

Dieses Prinzip nennt man Strong Reference. Solange es irgendwo noch eine Referenz auf einen Wert gibt, bleiben der Wert und die Referenz erhalten. Der Garbage Collector wird sie nicht abräumen.  

Dieses Verhalten ist in den meisten Fällen gewünscht. Wir denken als Entwickler:innen gar nicht darüber nach. 

 

Weak References 

Es gibt jedoch Fälle, in denen Elemente zu einer Datenstruktur zusammengefasst werden sollen oder wir zusätzliche Informationen zu einem Objekt speichern wollen, welches wir nicht selbst kontrollieren.  

Stellen wir uns etwa vor, wir haben mehrere fetch-Responses und wollen diese verarbeiten. Dazu wollen wir speichern, welche Responses bereits verarbeitet wurden. Wenn wir diese in einem Array speichern, erstellen wir eine neue Referenz. Das ist in diesem Fall nicht, was wir wollen, denn so kann die Response nicht vom Garbage Collector abgeräumt werden.  

Eine Datenstruktur, welche uns ermöglicht Elemente ohne neue Referenz hinzuzufügen, ist das WeakSet. Einem WeakSet kann per sogenannter Soft reference ein Element hinzugefügt werden. D. h. wir nutzen die bereits bestehende Referenz, um ein Element dem Set hinzuzufügen und es wird keine neue Referenz erzeugt. Genau mit dieser Referenz können wir dann auch prüfen, ob ein Element im Set vorhanden ist. Wird die Referenz null oder undefined, so wird das Element automatisch aus dem Set entfernt.      

Wenn wir Daten mit einem Element assoziieren wollen, können wir die Datenstruktur WeakMap verwenden. Dabei handelt es sich um eine Implementierung von Map, welche per Soft reference funktioniert. Um die Funktionsweise zu illustrieren habe ich ein kleines Beispielprogramm geschrieben:  

Am Anfang des Programms wird eine WeakMap initialisiert. Die Funktion add nimmt ein Objekt entgegen, welches zwei Zahlen a und b enthält. Zuerst prüft die Funktion, ob das übergebene Objekt bereits in der WeakMap vorhanden ist – falls ja, gibt es den in der Datenstruktur gespeicherten Wert zurück. Falls nicht, werden beide Werte addiert, in den Cache geschrieben und zurückgegeben.   

Beim ersten Aufruf ist die WeakMap leer, d. h. der Wert muss berechnet werden. Beim zweiten Aufruf kann das Ergebnis aus dem Cache gelesen werden. Danach löschen wir die Referenz auf das Objekt. Wenn wir daraufhin ein neues Objekt mit gleichen Werten anlegen, haben wir trotzdem eine neue Referenz, welche erneut in der WeakMap abgelegt werden muss.  

Hierbei ist wichtig auf das eigene „Referenzmanagement“ zu achten. Zur Illustration habe ich das Beispiel einmal geringfügig angepasst: 

In diesem Fall gibt es zwei Variablen, d. h. zwei Referenzen, numbers und numbers2, die auf den gleichen Wert zeigen.  

Beim ersten Aufruf ist die WeakMap leer, d. h. der Wert muss berechnet werden. Beim zweiten Aufruf kann das Ergebnis aus dem Cache gelesen werden, denn es handelt sich zwar um eine andere Variable, sie zeigt aber auf den gleichen Wert.  Danach löschen wir die Variable numbers, da es numbers2 aber noch gibt, liegt der Wert auch beim dritten Aufruf im Cache. Wird danach auch die zweite Variable auf undefined gesetzt und wir weisen numbers einen neuen Wert zu, muss das Ergebnis erneut berechnet werden.  

WeakMap und WeakSet sind aufgrund der Soft references nicht iterierbar. Wären sie iterierbar, so könnten Entwickler:innen bei der Iteration neue Referenzen zu den gespeicherten Werten definieren und der Vorteil, dass die Elemente bei Garbage Collection automatisch entfernt werden, wäre dahin. Tatsächlich bietet die API nicht einmal eine Möglichkeit, um die Anzahl der Elemente in der Datenstruktur abzurufen. 

 

Einsätze von WeakMaps in der Praxis

WeakMap und WeakSet bieten spannende Möglichkeiten: So könnten etwa Metainformationen zu einer DOM-Node gespeichert werden und wenn die DOM-Node gelöscht wird, werden automatisch die Metainformationen gelöscht. Oder das gezeigte WeakMap-Beispiel könnte ausgebaut werden, um das Ergebnis eines aufwendigen Methodenaufrufs zu speichern und so ein Cache in die Anwendung eingeführt werden. So nutzt etwa die Templating-Bibliothek lit-html WeakMaps, um HTML-Templates zu cachen.  

Trotzdem sollte der Einsatz dieser Datenstrukturen wohl überlegt sein. Führt man in seinen Code zusätzliche Referenzen ein, kann man den Mehrwert von WeakMap und WeakSet zunichte machen  


Dokumentation der Datenstrukturen im Mozilla Developer Network: 


Sie haben Interesse an unserer zweitägigen Grundlagen-Schulung zu JavaScript und TypeScript? Dann nehmen Sie gerne Kontakt zu uns auf.

Informationen anfordern!


 


zurück zur Blogübersicht

Diese Beiträge könnten Sie ebenfalls interessieren

Keinen Beitrag verpassen – viadee Blog abonnieren

Jetzt Blog abonnieren!

Kommentare

Christian Siebmanns

Christian Siebmanns

Christian Siebmanns ist Berater bei der viadee IT-Unternehmensberatung. Schwerpunkt seiner Arbeit ist der Einsatz in verschiedenen Kundenprojekten im Webumfeld. Christian ist Experte für Java und TypeScript.