A principal diferença é que os objetos suportam apenas chaves de cadeia de caracteres, enquanto o Maps suporta mais ou menos qualquer tipo de chave.
Se eu fizer obj[123] = true
e Object.keys(obj)
então eu vou conseguir, em ["123"]
vez de [123]
. Um mapa preservaria o tipo de chave e retornaria, o [123]
que é ótimo. Os mapas também permitem usar objetos como chaves. Tradicionalmente, para fazer isso, você teria que dar aos objetos algum tipo de identificador exclusivo para hash-los (acho que nunca vi nada parecido getObjectId
em JS como parte do padrão). Os mapas também garantem a preservação da ordem, por isso são melhores para a preservação e às vezes podem poupar a necessidade de fazer alguns tipos.
Entre mapas e objetos na prática, existem vários prós e contras. Os objetos obtêm vantagens e desvantagens, sendo fortemente integrados ao núcleo do JavaScript, o que os diferencia de um Mapa significativamente além da diferença no suporte principal.
Uma vantagem imediata é que você tem suporte sintático para objetos, facilitando o acesso a elementos. Você também tem suporte direto para isso com JSON. Quando usado como hash, é irritante obter um objeto sem nenhuma propriedade. Por padrão, se você deseja usar Objetos como uma tabela de hash, eles serão poluídos e você precisará frequentemente hasOwnProperty
acessá-los ao acessar propriedades. Você pode ver aqui como, por padrão, os objetos são poluídos e como criar objetos esperançosamente não poluídos para uso como hashes:
({}).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
A poluição nos objetos não é apenas algo que torna o código mais irritante, mais lento etc., mas também pode ter conseqüências potenciais para a segurança.
Os objetos não são tabelas de hash puro, mas estão tentando fazer mais. Você tem dores de cabeça hasOwnProperty
, como não conseguir obter o comprimento facilmente ( Object.keys(obj).length
) e assim por diante. Os objetos não devem ser usados apenas como mapas de hash, mas também como objetos dinâmicos extensíveis e, portanto, quando você os usa como puro problema de tabelas de hash, surgem.
Comparação / Lista de várias operações comuns:
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();
Existem algumas outras opções, abordagens, metodologias, etc. com altos e baixos (desempenho, conciso, portátil, extensível etc.). Os objetos são um pouco estranhos, sendo o núcleo da linguagem, então você tem muitos métodos estáticos para trabalhar com eles.
Além da vantagem do Google Maps preservar os tipos de chave, além de poder oferecer suporte a objetos como objetos, eles são isolados dos efeitos colaterais que muitos objetos têm. Um mapa é um puro hash, não há confusão sobre tentar ser um objeto ao mesmo tempo. Os mapas também podem ser facilmente estendidos com funções de proxy. Os objetos atualmente têm uma classe Proxy, no entanto, o desempenho e o uso da memória são desagradáveis, na verdade, criar seu próprio proxy que se parece com o Map for Objects atualmente tem um desempenho melhor que o Proxy.
Uma desvantagem substancial para o Maps é que eles não são suportados diretamente pelo JSON. A análise é possível, mas tem vários problemas:
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;
});
O exemplo acima apresentará um sério impacto no desempenho e também não suportará nenhuma chave de string. A codificação JSON é ainda mais difícil e problemática (essa é uma das muitas abordagens):
// 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())
});
};
Isso não é tão ruim se você estiver usando o Maps apenas, mas terá problemas quando estiver misturando tipos ou usando valores não escalares como chaves (não que o JSON seja perfeito com esse tipo de problema, pois é, referência a objetos circulares do IE). Eu não testei, mas é provável que isso prejudique gravemente o desempenho em comparação com o stringify.
Outras linguagens de script geralmente não apresentam problemas, pois possuem tipos não escalares explícitos para Map, Object e Array. O desenvolvimento da Web costuma ser um problema para tipos não escalares, nos quais você precisa lidar com coisas como o PHP mescla Matriz / Mapa com Objeto usando A / M para propriedades e JS mescla Mapa / Objeto com Matriz que estende M / O. A fusão de tipos complexos é o banimento do diabo de linguagens de script de alto nível.
Até o momento, essas questões são amplamente relacionadas à implementação, mas o desempenho das operações básicas também é importante. O desempenho também é complexo porque depende do mecanismo e do uso. Faça meus testes com um grão de sal, pois não posso descartar nenhum erro (tenho que me apressar). Você também deve executar seus próprios testes para confirmar, pois o meu examina apenas cenários simples muito específicos para fornecer apenas uma indicação aproximada. De acordo com testes no Chrome para objetos / mapas muito grandes, o desempenho dos objetos é pior por causa da exclusão, que é aparentemente de alguma forma proporcional ao número de chaves em vez de 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
O Chrome claramente tem uma grande vantagem em obter e atualizar, mas o desempenho da exclusão é horrível. Os mapas usam um pouco mais de memória nesse caso (sobrecarga), mas com apenas um objeto / mapa sendo testado com milhões de chaves, o impacto da sobrecarga nos mapas não é expresso bem. Com o gerenciamento de memória, os objetos também parecem liberar mais cedo se estou lendo o perfil corretamente, o que pode ser um benefício a favor dos objetos.
No Firefox, para esse benchmark específico, é uma história diferente:
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 salientar imediatamente que, neste benchmark em particular, a exclusão de objetos no Firefox não está causando problemas; no entanto, em outros benchmarks, ele causou problemas, especialmente quando existem muitas chaves, como no Chrome. Os mapas são claramente superiores no Firefox para grandes coleções.
No entanto, esse não é o fim da história, e muitos objetos ou mapas pequenos? Fiz uma referência rápida disso, mas não exaustiva (configuração / obtenção), com melhor desempenho com um pequeno número de chaves nas operações acima. Este teste é mais sobre memória e inicialização.
Map Create: 69 // new Map
Object Create: 34 // {}
Novamente, esses números variam, mas basicamente o Object tem uma boa vantagem. Em alguns casos, a vantagem dos objetos sobre os mapas é extrema (~ 10 vezes melhor), mas, em média, foi cerca de 2-3 vezes melhor. Parece que picos extremos de desempenho podem funcionar nos dois sentidos. Eu só testei isso no Chrome e na criação para analisar o uso e a sobrecarga de memória. Fiquei surpreso ao ver que, no Chrome, parece que o Maps com uma tecla usa cerca de 30 vezes mais memória do que os objetos com uma tecla.
Para testar muitos objetos pequenos com todas as operações acima (4 teclas):
Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139
Em termos de alocação de memória, eles se comportaram da mesma forma em termos de liberação / GC, mas o Map usou 5 vezes mais memória. Este teste utilizou 4 teclas, onde, como no último teste, apenas defini uma chave, o que explicaria a redução na sobrecarga de memória. Eu executei esse teste algumas vezes e o Mapa / Objeto é mais ou menos igual para o Chrome em termos de velocidade geral. No Firefox para objetos pequenos, há uma vantagem de desempenho definida sobre os mapas em geral.
Obviamente, isso não inclui as opções individuais que podem variar bastante. Eu não aconselharia micro-otimizar com esses números. O que você pode obter disso é que, como regra geral, considere o Maps com mais força para grandes armazenamentos de valores-chave e objetos para pequenos armazenamentos de valores-chave.
Além disso, a melhor estratégia com esses dois é implementá-lo e fazê-lo funcionar primeiro. Ao criar um perfil, é importante ter em mente que, às vezes, as coisas que você não acha que seriam lentas ao olhá-las podem ser incrivelmente lentas, devido às peculiaridades do mecanismo, como visto no caso de exclusão da chave do objeto.