nDieser Artikel ist Teil einer Serie:n
- n
- ES6 im Überblickn
- Block Scope und Konstantenn
- Maps, Sets und Weak Mapsn
- Generatorsn
- Rest, Spread und Defaultsn
- Promisesn
n
nn
nWährend andere Programmiersprachen Entwicklern eines ganzes Arsenal von Datenstrukturen zur Sammlungen von Objekten anbieten – Listen, Sets, Trees usw. – muss man sich in JavaScript seit jeher mit Arrays und Objekten begnügen. Diese magere Auswahl führt häufiger zu einem leisen Zähneknischen, denn die aus Bordmitteln gebauten Ersätze sind, wenn überhaupt machbar, umständlich und nicht besonders performant. Um an dieser Stelle für Linderung zu sorgen, führt ECMAScript 6 einen ganzen Haufen von neuen Cellections ein, von denen wir uns heute einmal Maps, Sets und Weak Maps genauer ansehen werden.nn
Maps
nn
nMaps sind ähnlich wie normale JavaScript-Objekte Paare aus Namen und Werten. Mit ihrer set()-Methode können neue Name-Wert-Paare in der Map gespeichert werden und mit get() können die Werte wieder ausgelesen werden:nn
var myMap = new Map();nmyMap.set('foo', 42);nmyMap.get('foo'); // 42
nn
nDer Clou hierbei: als Namen können alle JavaScript-Datentypen herhalten, nicht nur Strings:nn
var myMap = new Map();nmyMap.set(42, 'foo'); // Number als keynmyMap.set([1, 2, 3], 'bar'); // Array als keynmyMap.set(function(){}, 'baz'); // Funktion als keynmyMap.set(NaN, 'buh'); // Selbst NaN geht!
nn
nDamit wird es natürlich sehr viel leichter, Metadaten zu Objekten wie z.B. DOM-Nodes oder jQuery-Objekten zu verwalten. Weder ist es nötig, die Metadaten im Objekt selbst zu speichern (z.B. als data-*-Attribute in HTML-Elementen) noch muss man sich mit der Zuordnung eines Objekts zu einem Schlüssel in einem anderen Objekt abmühen – die Objekte selbst sind die Schlüssel!nn
nWichtig ist: als Schlüssel dienen wirklich die Objekte selbst und sie werden (fast) wie bei einem Vergleich mit === unterschieden. nn
// get() ergibt undefined - die Funktionen in Get undn// Set sind unterschiedliche ObjektenmyMap.set(function(){}, 'foo');nmyMap.get(function(){});nn// get() ergibt "bar" - die Funktion in Get und Setn// sind jeweils das gleiche Objektnvar fn = function(){};nmyMap.set(fn, 'bar');nmyMap.get(fn);
nn
nDie einzigen Ausnahmen sind an dieser Stelle NaN, das als identisch zu NaN gewertet wird (obwohl eigentlich NaN !== NaN gilt) und +0 bzw. -0, die unterschieden werden (obwohl eigentlich +0 === -0 gilt).nn
nNeben set() und get() haben Maps noch die folgenden drei Methoden:n
- n
myMap.has(key): Gibt an, ob unter dem Schlüsselkeyetwas gespeichert ist (Boolean)nmyMap.delete(key): Löscht die unter dem Schlüsselkeygespeicherten DatennmyMap.size(): Gibt die Anzahl der gespeicherten Name-Wert-Paare zurückn
nn
nEine Möglichkeit über Maps zu iterieren sehen die ECMAScript-Entwürfe zur Zeit nicht vor und auch kein Browser hat diesbezüglich etwas in petto. Dass in der Richtung noch nachgerüstet wird, ist aber nicht besonders unwahrscheinlich.nn
Sets
nn
nIn Sets können wie in einem Array beliebige Daten abgelegt werden, wobei allerdings jeder Wert nur einmal im Set vorhanden sein kann. Mit der has()-Methode kann geprüft werden, ob ein Wert in einem Set vorliegt:nn
var mySet = new Set();nmySet.add(42);nmySet.has(42); // true
nn
nOb ein Wert in einem Set vorhanden ist, ist eine einfache Ja/Nein-Frage. Führt man mehrmals mySet.add(42) aus, ändert sich nichts, denn der Wert 42 ist ja schon nach dem ersten Mal im Set vorhanden. Neben add() und has() kennen Sets noch die Methoden delete() zum Löschen eines Werts und size() zur Angabe der Anzahl der gespeicherten Datensätze.nn
nWie bei Maps auch werden die Werte in Sets mit === vergleichen mit den gleichen Ausnahmen für NaN sowie +0 und -0. Auch eine Möglichkeit über die gesammelten Daten zu iterieren fehlt Sets noch – Nachrüstung nicht ausgeschlossen.nnn
Weak Maps
n
nWeak Maps funktionieren wie normale Maps, verhindern aber nicht, dass ihre Schlüsselobjekte der automatischen Speicherbereinigung von JavaScript zum Opfer fallen. JS-Engines kümmern sich automatisch darum, Speicher für Objekte vorzuhalten und sie geben auch automatisch Speicher wieder frei, sobald ein Objekt nicht mehr gebraucht wird. Den Status „nicht mehr gebraucht“ erhält ein Objekt, sobald keine Variablen mehr auf es zeigen und es damit vom laufenden Programm nicht mehr erreicht werden kann. Im folgenden Beispielcode würde der vom Objekt belegte Speicher erst dann wieder frei gegeben, wenn die Variablen foo und bar nicht mehr auf das Objekt zeigen, dieses dadurch nicht mehr erreichbar und damit nicht mehr benötigt wird.nn
var foo = {};nvar bar = foo;
nn
nDie gleiche Lage gibt es auch beim Einsatz von Maps:nn
var myMap = new Map();nvar keyObj = { foo: 42 };nnmyMap.set(keyObj, 'foo'); // "foo" unter `keyObj` speichernnkeyObj = undefined; // `keyObj` referenziert nicht mehr das Objekt
nn
nDas Problem ist nur: wie werden wir jetzt das Objekt los, das unter keyObj gespeichert war? Da wir keine Referenz mehr in Form einer Variablen auf das Objekt haben, können wir myMap.delete() nicht benutzen. Andererseits wird das Objekt ja weiterhin von der Map referenziert und wird daher bis in alle Ewigkeit von der Speicherbereinigung verschont bleiben. Passiert so etwas häufiger, hat man ein hässliches Speicherproblem.nn
nWeak Maps schaffen hier Abhilfe, denn sie referenzieren Objekte in ihren Keys nur schwach. Wenn die einzige verbliebene Referenz auf ein Objekt der Schlüssel in einer Weak Map ist, darf die Speicherbereinigung das betreffende Objekt entfernen. Nehmen wir also den Problemcode von oben und ersetzen die Map durch eine Weak Map, löst sich das Speicherproblem in Luft auf:nn
var myMap = new WeakMap();nvar keyObj = { foo: 42 };nnmyMap.set(keyObj, 'foo'); // "foo" unter `keyObj` speichernnkeyObj = undefined; // `keyObj` referenziert nicht mehr das Objekt
nn
nSobald keyObj nicht mehr das Objekt referenziert, ist die einzig verbliebene Referenz die in der Weak Map. Da diese Referenz nur schwach ist, wird das Objekt beim nächsten Speicherbereinigungsdurchlauf gelöscht und der Speicher freigegeben. Naturgemäß kennen Weak Maps anders als Maps keine size()-Methode und auch eine Iterationsmöglichkeit wird es nicht geben.nnnn
Unterstützung und Ausblick
nn
nMaps, Sets und Weak Maps sind in Chrome Canary (bei aktiviertem Strict Mode) und Firefox ab Version 13 (Weak Maps ab Version 6) vorhanden. In Chrome muss unter about:flags der Punkt „Enable Experimental JavaScript“, Firefox und Firefox Mobile unterstützen die neuen Features ohne weiteres zutun. In Node.js können Weak Maps mit dem Startparameter --harmony_weakmaps aktiviert werden.nn
nKomplett fertig und stabil scheinen die neuen Datenstrukturen noch nicht zu sein, zumindest mit Iterationsmöglichkeiten für Sets und Maps ist noch zu rechnen. Andererseits gibt es nun in Chrome bzw. V8 und Spider Monkey (Firefox) bereits zwei Implementierungen – ganz so groß werden die Verwerfungen also wohl nicht mehr werden und zumindest dass die nächste ECMAScript-Version Maps, Sets und Weak Maps haben wird, scheint sicher. Weiterer Lesestoff:nn
- n
- Maps, Sets und Weak Maps bei MDNn
- Harmony-Proposals für Maps und Sets sowie für Weak Mapsn
- V8-Tests für Maps, Sets und Weak Mapsn
Schreibe einen Kommentar