Care sunt utilizările actuale ale WeakMap
structura de date introduse în ECMAScript 6?
Când o cheie de o harta slab creează o puternică referire la valoarea corespunzătoare, asigurându-se că o valoare care a fost introdus într-o harta slab va nu dispară atâta timp cât cheia este încă în viață, se poate't fi folosit pentru memo tabele, cache-uri sau orice altceva care le-ar folosi în mod normal referințe slabe, hărți cu slabe valori, etc. pentru.
Mi se pare ca acest lucru:
weakmap.set(key, value);
...este doar un mod de a spune acest lucru:
key.value = value;
Ce beton cazuri de utilizare sunt eu lipsesc?
WeakMaps oferi o modalitate de a extinde obiecte din exterior, fără a interfera cu colectarea gunoiului. Ori de câte ori doriți să se extindă un obiect, dar poate't pentru că acesta este sigilat - sau de la o sursă externă - o WeakMap pot fi aplicate.
Un WeakMap este o hartă (dicționar) unde tastele sunt slabe - care este, dacă toate trimiterile la key sunt pierdut și nu există mai multe referiri la valoare - value poate fi de gunoi colectate. Las's show această primă prin exemple, atunci explic un pic si termina în cele din urmă cu utilizarea reală.
Las's spun I'm, folosind un API care dă un anumit obiect:
var obj = getObjectFromLibrary();
Acum, eu am o metodă care folosește la obiect:
function useObj(obj){
doSomethingWith(obj);
}
Vreau să țineți evidența cât de multe ori metoda a fost numit cu un anumit obiect și să raporteze dacă se întâmplă mai mult de N ori. Cu naivitate unul ar fi că pentru a utiliza o Hartă:
var map = new Map(); // maps can have object keys
function useObj(obj){
doSomethingWith(obj);
var called = map.get(obj) || 0;
called++; // called one more time
if(called > 10) report(); // Report called more than 10 times
map.set(obj, called);
}
Aceasta funcționează, dar are o scurgere de memorie - ne acum ține evidența fiecare bibliotecă obiectul a trecut de la funcția pe care o păstrează la biblioteca de obiecte de la a fi gunoi colectate. În loc să - putem folosi o WeakMap
:
var map = new WeakMap(); // create a weak map
function useObj(obj){
doSomethingWith(obj);
var called = map.get(obj) || 0;
called++; // called one more time
if(called > 10) report(); // Report called more than 10 times
map.set(obj, called);
}
Și scurgeri de memorie este plecat.
Unele cazuri de utilizare, care altfel ar provoca o scurgere de memorie și sunt activate prin `WeakMap se includ:
Acesta poate fi folosit pentru a extinde un obiect din exterior. Las's dea o practice (adaptat, un fel de real - pentru a face un punct) exemplu din lumea reală a Node.js.
Las's spun te're Node.js și ai Promit
obiecte - acum doriți să urmăriți de toate, în prezent, respinse promisiuni - cu toate acestea, aveți nu doriți să păstrați-le de gunoi colectate în cazul în care nu există referințe la ele.
Acum, nu't_ doriți să adăugați proprietăți la obiectele native din motive evidente - deci're blocat. Dacă vă păstrați trimiteri la promisiunile pe care le're provocând o scurgere de memorie deoarece nu de colectare a gunoiului se poate întâmpla. Dacă tu nu't ține referințe apoi, puteți't de a salva informații suplimentare despre anumite promisiuni. Orice sistem care implică salvarea ID-ul de o promisiune, în mod inerent, înseamnă că ai nevoie de o trimitere la acesta.
WeakMaps înseamnă că tastele sunt slabe. Nu există modalități de a enumera o harta slab sau pentru a obține toate valorile sale. Într-un slab hartă, puteți stoca datele bazate pe o cheie și atunci când cheia se devine gunoi colectate deci, nu valorile.
Acest lucru înseamnă că, având o promisiune puteți stoca stat despre asta - și de acel obiect poate fi încă de gunoi colectate. Mai târziu, dacă veți obține o referință la un obiect, puteți verifica dacă aveți orice stat cu privire la acesta și să-l raporteze.
Acest lucru a fost folosit pentru a pune în aplicare netratată respingere cârlige de Petka Antonov ca aceasta:
process.on('unhandledRejection', function(reason, p) {
console.log("Unhandled Rejection at: Promise ", p, " reason: ", reason);
// application specific logging, throwing an error, or other logic here
});
Vom păstra informații despre promisiuni într-o hartă și poate ști când a fost respinsă o promisiune a fost manipulat.
Un caz de utilizare ar putea fi să-l folosească ca un dicționar pentru ascultători, am un coleg care a facut asta. Este foarte util pentru orice ascultător este direct indreptat cu acest mod de a face lucrurile. La revedere ascultător.pe
.
Dar de o mai abstract punct de vedere, WeakMap
este deosebit de puternic pentru a dematerializa acces la practic nimic, nu't nevoie de un spațiu de nume pentru a izola membrii săi, deoarece este deja implicat de natura acestei structuri. Am'm destul de sigur că ai putea face unele majore îmbunătățiri de memorie prin înlocuirea awkwards redundante obiect chei (chiar dacă deconstruirea functioneaza pentru tine).
Eu acum seama mea sublinia nu este exact cel mai bun mod de a aborda problema și cum Benjamin Gruenbaum a subliniat (a verifica afară de răspunsul lui, dacă-l's nu este deja mai presus de a mea :p), această problemă nu ar fi putut fi rezolvate cu un regular "Harta", deoarece s-ar fi scurs, astfel, principala forță de WeakMap` este că nu interferează cu colectarea gunoiului având în vedere că ele nu păstrează o referință.
Aici este codul actual de colegul meu (datorită l pentru sharing)
Sursa completa aici, l's despre ascultatorii de management despre care am vorbit mai sus (puteți, de asemenea, să ia o privire la specificatii)
var listenableMap = new WeakMap();
export function getListenable (object) {
if (!listenableMap.has(object)) {
listenableMap.set(object, {});
}
return listenableMap.get(object);
}
export function getListeners (object, identifier) {
var listenable = getListenable(object);
listenable[identifier] = listenable[identifier] || [];
return listenable[identifier];
}
export function on (object, identifier, listener) {
var listeners = getListeners(object, identifier);
listeners.push(listener);
}
export function removeListener (object, identifier, listener) {
var listeners = getListeners(object, identifier);
var index = listeners.indexOf(listener);
if(index !== -1) {
listeners.splice(index, 1);
}
}
export function emit (object, identifier, ...args) {
var listeners = getListeners(object, identifier);
for (var listener of listeners) {
listener.apply(object, args);
}
}
WeakMap
funcționează bine pentru încapsularea și ascunderea de informații
WeakMap
este disponibil numai pentru ES6 și mai sus. O WeakMap
este o colecție de perechi de valori-cheie și în cazul în care cheia trebuie să fie un obiect. În următorul exemplu, vom construi o WeakMap cu două elemente:
var map = new WeakMap();
var pavloHero = {first: "Pavlo", last: "Hero"};
var gabrielFranco = {first: "Gabriel", last: "Franco"};
map.set(pavloHero, "This is Hero");
map.set(gabrielFranco, "This is Franco");
console.log(map.get(pavloHero));//This is Hero
Am folosit set () metoda pentru a defini o asociere între un obiect și un alt element (un șir de caractere în cazul nostru). Am folosit
get () metoda pentru a prelua elementul asociate cu un obiect. Cel mai interesant aspect al WeakMap este faptul ca are o slabă referință la cheia în interiorul hărții. O slabă referință înseamnă că, dacă obiectul este distrus, colectorul de gunoi va elimina întreaga intrare din
WeakMap`, prin urmare, eliberarea de memorie.
var TheatreSeats = (function() {
var priv = new WeakMap();
var _ = function(instance) {
return priv.get(instance);
};
return (function() {
function TheatreSeatsConstructor() {
var privateMembers = {
seats: []
};
priv.set(this, privateMembers);
this.maxSize = 10;
}
TheatreSeatsConstructor.prototype.placePerson = function(person) {
_(this).seats.push(person);
};
TheatreSeatsConstructor.prototype.countOccupiedSeats = function() {
return _(this).seats.length;
};
TheatreSeatsConstructor.prototype.isSoldOut = function() {
return _(this).seats.length >= this.maxSize;
};
TheatreSeatsConstructor.prototype.countFreeSeats = function() {
return this.maxSize - _(this).seats.length;
};
return TheatreSeatsConstructor;
}());
})()
Eu folosesc WeakMap pentru cache-ul de griji-free memoization de funcții care ia în imuabile obiecte ca parametru.
Memoization este în mod fantezist de a spune "după ce a calcula valoarea, cache, astfel încât să don't au pentru a calcula din nou".
Aici's un exemplu:
// using immutable.js from here https://facebook.github.io/immutable-js/
const memo = new WeakMap();
let myObj = Immutable.Map({a: 5, b: 6});
function someLongComputeFunction (someImmutableObj) {
// if we saved the value, then return it
if (memo.has(someImmutableObj)) {
console.log('used memo!');
return memo.get(someImmutableObj);
}
// else compute, set, and return
const computedValue = someImmutableObj.get('a') + someImmutableObj.get('b');
memo.set(someImmutableObj, computedValue);
console.log('computed value');
return computedValue;
}
someLongComputeFunction(myObj);
someLongComputeFunction(myObj);
someLongComputeFunction(myObj);
// reassign
myObj = Immutable.Map({a: 7, b: 8});
someLongComputeFunction(myObj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/immutable/3.8.1/immutable.min.js"></script>
Câteva lucruri de reținut:
Slab Hărți pot fi utilizate pentru a stoca metadate despre DOM elemente, fără a interfera cu colectarea gunoiului sau de a face colegii nebun la cod. De exemplu, ai putea să le utilizeze pentru a indice numeric tuturor elementelor într-o pagină web.
var elements = document.getElementsByTagName('*'),
i = -1, len = elements.length;
while (++i !== len) {
// Production code written this poorly makes me want to cry:
elements[i].lookupindex = i;
elements[i].elementref = [];
elements[i].elementref.push( elements[Math.pow(i, 2) % len] );
}
// Then, you can access the lookupindex's
// For those of you new to javascirpt, I hope the comments below help explain
// how the ternary operator (?:) works like an inline if-statement
document.write(document.body.lookupindex + '<br />' + (
(document.body.elementref.indexOf(document.currentScript) !== -1)
? // if(document.body.elementref.indexOf(document.currentScript) !== -1){
"true"
: // } else {
"false"
) // }
);
var DOMref = new WeakMap(),
__DOMref_value = Array,
__DOMref_lookupindex = 0,
__DOMref_otherelement = 1,
elements = document.getElementsByTagName('*'),
i = -1, len = elements.length, cur;
while (++i !== len) {
// Production code written this greatly makes me want to 😊:
cur = DOMref.get(elements[i]);
if (cur === undefined)
DOMref.set(elements[i], cur = new __DOMref_value)
cur[__DOMref_lookupindex] = i;
cur[__DOMref_otherelement] = new WeakSet();
cur[__DOMref_otherelement].add( elements[Math.pow(i, 2) % len] );
}
// Then, you can access the lookupindex's
cur = DOMref.get(document.body)
document.write(cur[__DOMref_lookupindex] + '<br />' + (
cur[__DOMref_otherelement].has(document.currentScript)
? // if(cur[__DOMref_otherelement].has(document.currentScript)){
"true"
: // } else {
"false"
) // }
);
Diferența poate arata neglijabil, în afară de faptul că weakmap versiune este mai lung, cu toate acestea, există o diferență majoră între cele două bucăți de cod prezentate mai sus. În primul fragment de cod, fără a slabi hărți, bucata de cod magazine trimiterile de orice fel care între elementele DOM. Acest lucru previne DOM elemente de gunoi colectate. Matematica.pow(i, 2) % len]
poate părea ciudat că nimeni nu s-ar folosi, dar cred că din nou: o mulțime de codul de producție a DOM referințe care topaie peste tot în document. Acum, pentru cea de-a doua bucată de cod, pentru că toate referirile la elemente sunt slab, atunci când o elimina un nod, browser-ul este capabil de a determina care nodul nu este folosit (nu putea fi atins de cod), și, astfel, a șterge din memorie. Motivul pentru care ar trebui să fie preocupat de utilizarea memoriei, și ancore de memorie (cum ar fi primul fragment de cod în cazul în care elemente neutilizate sunt păstrate în memorie) pentru că este mai multă memorie de utilizare înseamnă mai mult de browser-ul GC-încearcă (să încercați pentru a elibera memorie pentru a evita un accident de browser-ul) înseamnă mai lent experiență de navigare și, uneori, un accident de browser-ul.
Ca pentru un polyfill pentru acestea, aș recomanda biblioteca mea (găsite aici @ github). Este un foarte ușor de bibliotecă, care vor pur și simplu polyfill fără nici din cale-prea-complex de cadre s-ar putea găsi în alte polyfills.
~ Codificare fericit!