Som vi ved fra kapitlet Oprydning af affald (Garbage collection), holder JavaScript-motoren en vrdi i hukommelsen, s lnge den er tilgngelig og potentielt kan bruges.
For eksempel:
let john = { name: "John" };
// objektet kan tilgs, john er referencen til det
// overskriv referencen
john = null;
// objektet vil blive fjernet fra hukommelsen
Normalt er egenskaber af et objekt eller elementer af et array eller andre datastrukturer tilgngelige og holdes i hukommelsen, s lnge den datastruktur er i hukommelsen.
For eksempel, hvis vi putter et objekt i et array, s lnge arrayet er i live, vil objektet ogs vre i live, selvom der ikke er andre referencer til det.
Som dette:
let john = { name: "John" };
let array = [ john ];
john = null; // overskriv referencen
// objektet, der tidligere blev refereret til af john, er gemt inde i arrayet
// derfor vil det ikke blive fjernet af garbage-collectoren
// vi kan f det som array[0]
P samme mde, hvis vi bruger et objekt som ngle i et almindeligt Map, s lnge Map eksisterer, eksisterer det objekt ogs. Det optager hukommelse og kan ikke blive fjernet af garbage-collectoren.
For eksempel:
let john = { name: "John" };
let map = new Map();
map.set(john, "...");
john = null; // overskriv referencen
// john er gemt inde i map'et,
// vi kan f det ved at bruge map.keys()
WeakMap er fundamentalt anderledes i den henseende. Det forhindrer ikke garbage-collecting af ngleobjekter.
Lad os se, hvad det betyder p eksempler.
WeakMap
Den frste forskel mellem Map og WeakMap er, at ngler skal vre objekter, ikke primitive vrdier:
let weakMap = new WeakMap();
let obj = {};
weakMap.set(obj, "ok"); // virker fint (objekt ngle)
// kan ikke bruge en streng som ngle
weakMap.set("test", "Whoops"); // Fejl, fordi "test" ikke er et objekt
Nu, hvis vi bruger et objekt som ngle i det, og der ikke er andre referencer til det objekt vil det automatisk blive fjernet fra hukommelsen (og fra kortet).
let john = { name: "John" };
let weakMap = new WeakMap();
weakMap.set(john, "...");
john = null; // overskriv referencen
// john fjernes fra hukommelsen!
Sammenlign det med det almindelige Map-eksempel ovenfor. Nu, hvis john kun eksisterer som nglen til WeakMap vil det automatisk blive slettet fra kortet (og hukommelsen).
WeakMap understtter ikke iteration og metoderne keys(), values(), entries(), s der er ingen mde at f alle ngler eller vrdier fra det.
WeakMap har kun flgende metoder:
Hvorfor den begrnsning? Det er af tekniske grunde. Hvis et objekt har mistet alle andre referencer (som john i koden ovenfor), s skal det automatisk blive garbage-collected. Men teknisk set er det ikke prcist specificeret hvornr oprydningen sker.
Det er JavaScript-motoren, der beslutter det. Den kan vlge at udfre oprydningen med det samme eller vente og gre det senere, nr flere sletninger sker. S teknisk set er det aktuelle elementantal i en WeakMap ikke kendt. Motoren kan have ryddet op eller ej, eller gjort det delvist. Af den grund understttes metoder, der fr adgang til alle ngler/vrdier, ikke.
OK, men hvor har vi brug for en sdan datastruktur?
Brugsomrde: yderligere data
Hovedomrdet for anvendelse af WeakMap er en yderligere datalagring.
Hvis vi arbejder med et objekt, der tilhrer en anden kode, mske endda et tredjepartsbibliotek, og gerne vil gemme nogle data, der er knyttet til det, som kun skal eksistere, mens objektet er i live s er WeakMap prcis det, der er brug for.
Vi lgger dataene i en WeakMap, bruger objektet som ngle, og nr objektet bliver garbage collected, forsvinder disse data automatisk ogs.
weakMap.set(john, "secret documents");
// hvis john dr, vil de hemmelige dokumenter automatisk blive delagt
Lad os se p et eksempel.
For eksempel har vi kode, der holder styr p antallet af besg for brugere. Oplysningerne gemmes i et map: et brugerobjekt er nglen, og antallet af besg er vrdien. Nr en bruger forlader (dets objekt bliver garbage collected), nsker vi ikke lngere at gemme deres besgstlling.
Her er et eksempel p en tllefunktion med Map:
// visitsCount.js
let visitsCountMap = new Map(); // map: user => besgstller
// Forg besgstlleren
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
Og her er en anden del af koden, mske en anden fil, der bruger den:
// main.js
let john = { name: "John" };
countUser(john); // tller hans besg
// senere forlader john os
john = null;
Nu burde john-objektet blive garbage collected, men det forbliver i hukommelsen, da det er en ngle i visitsCountMap.
Vi har brug for at rydde op i visitsCountMap, nr vi fjerner brugere, ellers vil det vokse i hukommelsen p ubestemt tid. En sdan oprydning kan blive en kedelig opgave i komplekse arkitekturer.
Vi kan undg det ved at skifte til WeakMap i stedet:
// visitsCount.js
let visitsCountMap = new WeakMap(); // weakmap: user => besgstller
// Forg besgstlleren
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
Nu behver vi ikke at rydde op i visitsCountMap. Nr john-objektet bliver utilgngeligt, bortset fra som en ngle i WeakMap, fjernes det fra hukommelsen sammen med oplysningerne ved den ngle fra WeakMap.
Brugsscenarie: caching
Et andet almindeligt eksempel er caching. Vi kan gemme (cache) resultater fra en funktion, s fremtidige kald med det samme objekt kan genbruge det.
For at opn det kan vi bruge Map (ikke optimalt scenarie):
// cache.js
let cache = new Map();
// udregn og husk resultatet
function process(obj) {
if (!cache.has(obj)) {
let result = /* beregning af resultatet for */ obj;
cache.set(obj, result);
return result;
}
return cache.get(obj);
}
// Nu bruger vi process() i en anden fil:
// main.js
let obj = {/* lad os sige, vi har et objekt */};
let result1 = process(obj); // udregn resultat
// ...senere, fra et andet sted i koden...
let result2 = process(obj); // husket resultat taget fra cache
// ...senere, nr objektet ikke lngere er ndvendigt:
obj = null;
alert(cache.size); // 1 (Av! Objektet er stadig i cache og optager hukommelse!)
For flere kald af process(obj) med det samme objekt, udregnes resultatet kun frste gang, og derefter hentes det blot fra cache. Ulempen er, at vi skal rydde op i cache, nr objektet ikke lngere er ndvendigt.
Hvis vi erstatter Map med WeakMap, forsvinder dette problem. Det cachede resultat fjernes automatisk fra hukommelsen, nr objektet bliver garbage collected.
// cache.js
let cache = new WeakMap();
// udregn og husk resultatet
function process(obj) {
if (!cache.has(obj)) {
let result = /* beregning af resultatet for */ obj;
cache.set(obj, result);
return result;
}
return cache.get(obj);
}
// main.js
let obj = {/* et object */};
let result1 = process(obj);
let result2 = process(obj);
// ...senere, nr objektet ikke lngere er ndvendigt:
obj = null;
// Kan ikke f cache.size, da det er en WeakMap,
// men det er 0 eller vil snart vre 0
// Nr obj bliver garbage collected, fjernes cachede data ogs
WeakSet
WeakSet opfrer sig p lignende mde:
- Det er lig med
Set, men vi m kun tilfje objekter tilWeakSet(ikke primitive vrdier). - Et objekt findes i sttet, s lnge det er tilgngeligt fra et andet sted.
- Ligesom
Setundersttter detadd,hasogdelete, men ikkesize,keys()og iterationer.
Som weak fungerer det ogs som en ekstra lagerplads. Men ikke til vilkrlige data, snarere til ja/nej fakta. Et medlemskab i WeakSet kan betyde noget om objektet.
For eksempel kan vi tilfje brugere til WeakSet for at holde styr p dem, der har besgt vores side:
let visitedSet = new WeakSet();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
visitedSet.add(john); // John besgte os
visitedSet.add(pete); // S Pete
visitedSet.add(john); // John igen
// visitedSet har nu 2 brugere
// tjek om John besgte?
alert(visitedSet.has(john)); // true
// tjek om Mary besgte?
alert(visitedSet.has(mary)); // false
john = null;
// visitedSet vil blive ryddet automatisk
Den vigtigste begrnsning ved WeakMap og WeakSet er fravret af iterationer og manglende mulighed for at f alt det aktuelle indhold. Det kan virke upraktisk, men forhindrer ikke WeakMap/WeakSet i at udfre deres hovedopgave at vre en yderligere lagerplads for data til objekter, som opbevares/administreres et andet sted.
Opsummering
WeakMap er en Map-lignende samling, der kun tillader objekter som ngler og fjerner dem sammen med den tilknyttede vrdi, nr de ikke lngere er tilgngelige p anden vis.
WeakSet er en Set-lignende samling, der kun gemmer objekter og fjerner dem, nr de ikke lngere er tilgngelige p anden vis.
Deres strste fordel er, at de har svage referencer til objekter, s de nemt kan fjernes af garbage collectoren.
Det kommer dog med den ulempe, at de ikke understtter clear, size, keys, values
WeakMap og WeakSet bruges som sekundre datastrukturer ud over den primre objektlagring. Nr objektet fjernes fra den primre lagring, hvis det kun findes som ngle i en WeakMap eller i en WeakSet, vil det blive ryddet automatisk.
Kommentarer
<code>-taggen, for flere linjer - omslut dem i<pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen)