[ Web Proxy ]
URL:
Viewing: https://da.javascript.info/weakmap-weakset [Back]  [Original]

WeakMap og WeakSet
DA

Vi nsker at gre dette open source-projekt tilgngeligt for folk over hele verden.

Hjlp med at overstte indholdet af denne tutorial til dit sprog!

    Sg p Javascript.info:
    Sg i tutorialen:
    Lyst temaMrkt tema
    DanskEnglishEspaolFranaisIndonesiaItalianoTrkeOzbek

    WeakMap og WeakSet

    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 til WeakSet (ikke primitive vrdier).
    • Et objekt findes i sttet, s lnge det er tilgngeligt fra et andet sted.
    • Ligesom Set understtter det add, has og delete, men ikke size, 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.

    Opgaver

    vigtighed: 5

    Her er et array af beskeder:

    let messages = [
      {text: "Hej", from: "John"},
      {text: "Hvordan gr det?", from: "John"},
      {text: "Vi ses snart", from: "Alice"}
    ];

    Din kode kan tilg det, men beskederne styres af en andens kode. Nye beskeder tilfjes, gamle fjernes regelmssigt af den kode, og du kender ikke de prcise tidspunkter, hvor det sker.

    Hvilken datastruktur kunne du bruge til at gemme information om, hvorvidt beskeden er blevet lst? Strukturen skal vre velegnet til at give svaret blev den lst? for det givne beskedobjekt.

    P.S. Nr en besked fjernes fra messages, skal den ogs forsvinde fra din struktur.

    P.P.S. Vi br ikke ndre beskedobjekterne ved at tilfje vores egne egenskaber til dem. Da de styres af en andens kode, kan det fre til unskede konsekvenser.

    lsning

    Lets store read messages in WeakSet:

    let messages = [
      {text: "Hello", from: "John"},
      {text: "How goes?", from: "John"},
      {text: "See you soon", from: "Alice"}
    ];
    
    let readMessages = new WeakSet();
    
    // to beskeder er lst
    readMessages.add(messages[0]);
    readMessages.add(messages[1]);
    // readMessages har 2 elementer
    
    // ...lad os lse den frste besked igen!
    readMessages.add(messages[0]);
    // readMessages stadig har 2 unikke elementer
    
    // svar: var message[0] lst?
    alert("Er besked 0 lst: " + readMessages.has(messages[0])); // true
    
    messages.shift();
    // nu har readMessages 1 element (teknisk set kan hukommelsen ryddes senere)

    Et WeakSet tillader at gemme et st af beskeder og nemt kontrollere, om en besked findes i det.

    Det rydder automatisk op i sig selv. Ulempen er, at vi ikke kan iterere over det, og vi kan ikke f alle lste beskeder direkte fra det. Men vi kan gre det ved at iterere over alle beskeder og filtrere dem, der er i sttet.

    En anden, forskellig lsning kunne vre at tilfje en egenskab som message.isRead=true til en besked, efter den er lst. Da beskedobjekter styres af en anden kode, er det generelt ikke anbefalet, men vi kan bruge en symbolsk egenskab for at undg konflikter.

    Som dette:

    // den symbolske egenskab er kun kendt af vores kode
    let isRead = Symbol("isRead");
    messages[0][isRead] = true;

    Nu vil 3de-parts kode sandsynligvis ikke se vores ekstra egenskab.

    Selvom symboler reducerer sandsynligheden for problemer, er brugen af WeakSet bedre fra et arkitektonisk synspunkt.

    vigtighed: 5

    Der er et array af beskeder som i forrige opgave. Situationen er lignende.

    let messages = [
      {text: "Hej", from: "John"},
      {text: "Hvordan gr det?", from: "John"},
      {text: "Vi ses snart", from: "Alice"}
    ];

    Sprgsmlet nu er: hvilken datastruktur vil du foresl til at gemme informationen: hvornr blev beskeden lst?.

    I den forrige opgave skulle vi kun gemme ja/nej-faktumet. Nu skal vi gemme datoen, og den skal kun forblive i hukommelsen, indtil beskeden bliver garbage collected.

    P.S. Datoer kan gemmes som objekter af den indbyggede Date-klasse, som vi dkker senere.

    lsning

    For at gemme en dato, kan vi bruge WeakMap:

    let messages = [
      {text: "Hej", from: "John"},
      {text: "Hvordan gr det?", from: "John"},
      {text: "Vi ses snart", from: "Alice"}
    ];
    
    let readMap = new WeakMap();
    
    readMap.set(messages[0], new Date(2017, 1, 1));
    // Date objektet vil vi studere senere
    Tutorial-oversigt

    Kommentarer

    ls dette fr du kommenterer
    • Hvis du har forslag til forbedringer - s opret venligst et GitHub-issue eller en pull request i stedet for at kommentere.
    • Hvis du ikke forstr noget i artiklen - s uddyb venligst.
    • For at indstte f ord kode, brug <code>-taggen, for flere linjer - omslut dem i <pre>-tag, for mere end 10 linjer - brug en sandbox (plnkr, jsbin, codepen)

    Web Proxy Viewer  |  New URL  |  Original Page