La differenza fondamentale è che gli oggetti supportano solo chiavi di stringa in cui le mappe supportano più o meno qualsiasi tipo di chiave.
Se lo faccio obj[123] = true
e poi Object.keys(obj)
otterrò ["123"]
piuttosto che [123]
. Una mappa manterrebbe il tipo di chiave e restituirà il [123]
che è fantastico. Le mappe consentono inoltre di utilizzare gli oggetti come chiavi. Tradizionalmente per fare questo dovresti dare agli oggetti una sorta di identificatore univoco per cancellarli (non credo di aver mai visto qualcosa di simile getObjectId
in JS come parte dello standard). Le mappe garantiscono anche la conservazione dell'ordine, quindi sono sempre meglio per la conservazione e talvolta possono farti risparmiare la necessità di fare alcuni tipi.
Tra mappe e oggetti in pratica ci sono diversi pro e contro. Gli oggetti ottengono sia vantaggi che svantaggi essendo strettamente integrati nel nucleo di JavaScript, il che li distingue in modo significativo da Map oltre la differenza nel supporto chiave.
Un vantaggio immediato è che hai il supporto sintattico per gli oggetti che semplifica l'accesso agli elementi. Hai anche il supporto diretto per esso con JSON. Se usato come hash è fastidioso ottenere un oggetto senza alcuna proprietà. Per impostazione predefinita, se si desidera utilizzare gli oggetti come tabella hash, questi verranno inquinati e spesso sarà necessario richiamarli hasOwnProperty
quando si accede alle proprietà. Puoi vedere qui come per impostazione predefinita gli oggetti sono inquinati e come creare oggetti sperabilmente non inquinati da usare come hash:
({}).toString
toString() { [native code] }
JSON.parse('{}').toString
toString() { [native code] }
(Object.create(null)).toString
undefined
JSON.parse('{}', (k,v) => (typeof v === 'object' && Object.setPrototypeOf(v, null) ,v)).toString
undefined
L'inquinamento sugli oggetti non è solo qualcosa che rende il codice più fastidioso, più lento, ecc., Ma può anche avere potenziali conseguenze per la sicurezza.
Gli oggetti non sono pure tabelle hash ma stanno provando a fare di più. Hai mal di testa come hasOwnProperty
, non essere in grado di ottenere facilmente la lunghezza ( Object.keys(obj).length
) e così via. Gli oggetti non sono pensati per essere usati esclusivamente come mappe hash ma anche come oggetti dinamici estensibili e quindi quando li usi come pure tabelle hash si presentano problemi.
Confronto / Elenco di varie operazioni comuni:
Object:
var o = {};
var o = Object.create(null);
o.key = 1;
o.key += 10;
for(let k in o) o[k]++;
var sum = 0;
for(let v of Object.values(m)) sum += v;
if('key' in o);
if(o.hasOwnProperty('key'));
delete(o.key);
Object.keys(o).length
Map:
var m = new Map();
m.set('key', 1);
m.set('key', m.get('key') + 10);
m.foreach((k, v) => m.set(k, m.get(k) + 1));
for(let k of m.keys()) m.set(k, m.get(k) + 1);
var sum = 0;
for(let v of m.values()) sum += v;
if(m.has('key'));
m.delete('key');
m.size();
Ci sono alcune altre opzioni, approcci, metodologie, ecc. Con alti e bassi variabili (prestazioni, terse, portatili, estensibili, ecc.). Gli oggetti sono un po 'strani essendo fondamentali nel linguaggio, quindi hai molti metodi statici per lavorare con loro.
Oltre al vantaggio di Mappe di preservare i tipi di chiave e di essere in grado di supportare oggetti come gli oggetti, sono isolati dagli effetti collaterali che gli oggetti hanno. Una mappa è un puro hash, non c'è confusione nel cercare di essere un oggetto allo stesso tempo. Le mappe possono anche essere facilmente estese con le funzioni proxy. Gli oggetti attualmente hanno una classe Proxy, tuttavia le prestazioni e l'utilizzo della memoria sono tristi, infatti la creazione del proprio proxy che assomiglia a Map for Objects attualmente funziona meglio del Proxy.
Uno svantaggio sostanziale per Maps è che non sono supportati direttamente con JSON. L'analisi è possibile ma ha diversi blocchi:
JSON.parse(str, (k,v) => {
if(typeof v !== 'object') return v;
let m = new Map();
for(k in v) m.set(k, v[k]);
return m;
});
Quanto sopra introdurrà un grave impatto sulle prestazioni e non supporterà alcuna chiave di stringa. La codifica JSON è ancora più difficile e problematica (questo è uno dei tanti approcci):
// An alternative to this it to use a replacer in JSON.stringify.
Map.prototype.toJSON = function() {
return JSON.stringify({
keys: Array.from(this.keys()),
values: Array.from(this.values())
});
};
Questo non è così male se stai semplicemente usando Maps ma avrai problemi quando mescoli tipi o usi valori non scalari come chiavi (non che JSON sia perfetto con quel tipo di problema com'è, riferimento circolare agli oggetti IE). Non l'ho testato, ma è probabile che danneggerà gravemente le prestazioni rispetto a stringere.
Altri linguaggi di scripting spesso non presentano tali problemi in quanto hanno tipi espliciti non scalari per Map, Object e Array. Lo sviluppo Web è spesso una seccatura con tipi non scalari in cui devi fare i conti con cose come PHP unisce Array / Map con Object usando A / M per le proprietà e JS unisce Map / Object con Array che estende M / O. La fusione di tipi complessi è la rovina del diavolo di linguaggi di scripting di alto livello.
Finora si tratta in gran parte di problemi relativi all'implementazione, ma anche le prestazioni per le operazioni di base sono importanti. Le prestazioni sono anche complesse perché dipendono dal motore e dall'utilizzo. Fai i miei test con un granello di sale perché non posso escludere alcun errore (devo affrettarmi). Dovresti anche eseguire i tuoi test per confermare mentre il mio esamina solo scenari semplici molto specifici per dare solo un'indicazione approssimativa. Secondo i test in Chrome per oggetti / mappe molto grandi, le prestazioni degli oggetti sono peggiori a causa dell'eliminazione che è apparentemente in qualche modo proporzionata al numero di chiavi anziché a O (1):
Object Set Took: 146
Object Update Took: 7
Object Get Took: 4
Object Delete Took: 8239
Map Set Took: 80
Map Update Took: 51
Map Get Took: 40
Map Delete Took: 2
Chrome ha chiaramente un grande vantaggio nell'ottenere e aggiornare, ma le prestazioni di eliminazione sono orribili. In questo caso, le mappe utilizzano una quantità minima di memoria (overhead) ma con un solo oggetto / mappa testato con milioni di chiavi, l'impatto dell'overhead per le mappe non viene espresso bene. Con la gestione della memoria anche gli oggetti sembrano liberarsi prima se leggo correttamente il profilo, il che potrebbe essere un vantaggio a favore degli oggetti.
In Firefox per questo particolare benchmark è una storia diversa:
Object Set Took: 435
Object Update Took: 126
Object Get Took: 50
Object Delete Took: 2
Map Set Took: 63
Map Update Took: 59
Map Get Took: 33
Map Delete Took: 1
Devo immediatamente sottolineare che in questo particolare benchmark l'eliminazione di oggetti in Firefox non sta causando alcun problema, tuttavia in altri benchmark ha causato problemi soprattutto quando ci sono molti tasti proprio come in Chrome. Le mappe sono chiaramente superiori in Firefox per raccolte di grandi dimensioni.
Tuttavia questa non è la fine della storia, che dire di molti piccoli oggetti o mappe? Ho fatto un rapido benchmark di questo, ma non esaustivo (impostazione / acquisizione) dei quali funziona meglio con un piccolo numero di tasti nelle operazioni sopra. Questo test riguarda più la memoria e l'inizializzazione.
Map Create: 69 // new Map
Object Create: 34 // {}
Anche in questo caso queste cifre variano, ma sostanzialmente Object ha un buon vantaggio. In alcuni casi il vantaggio di Oggetti su mappe è estremo (~ 10 volte meglio) ma in media era circa 2-3 volte migliore. Sembra che picchi di prestazioni estreme possano funzionare in entrambi i modi. L'ho testato solo in Chrome e nella creazione per profilare l'utilizzo della memoria e il sovraccarico. Sono stato piuttosto sorpreso di vedere che in Chrome sembra che Maps con una chiave utilizzi circa 30 volte più memoria degli Oggetti con una chiave.
Per testare molti piccoli oggetti con tutte le operazioni di cui sopra (4 tasti):
Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139
In termini di allocazione della memoria, questi si comportarono allo stesso modo in termini di liberazione / GC, ma Map usò 5 volte più memoria. Questo test ha utilizzato 4 tasti in cui, come nell'ultimo test, ho impostato solo un tasto in modo da spiegare la riduzione del sovraccarico di memoria. Ho eseguito questo test alcune volte e Map / Object sono più o meno in generale per Chrome in termini di velocità complessiva. In Firefox per piccoli oggetti c'è un netto vantaggio prestazionale rispetto alle mappe in generale.
Questo ovviamente non include le singole opzioni che potrebbero variare in modo selvaggio. Non consiglierei la microottimizzazione con queste cifre. Ciò che puoi ottenere da questo è che come regola empirica, considera Maps più fortemente per archivi di valori chiave molto grandi e oggetti per archivi di valori chiave piccoli.
Oltre a ciò la migliore strategia con questi due è di implementarlo e farlo funzionare prima. Quando si profila, è importante tenere presente che a volte le cose che non si penserebbero lente quando le si guarda possono essere incredibilmente lente a causa delle stranezze del motore, come si vede con il caso di cancellazione della chiave dell'oggetto.