Mapa vs objeto em JavaScript


290

Acabei de descobrir chromestatus.com e, depois de perder várias horas do meu dia, encontrei esta entrada de recurso :

Mapa: objetos de mapa são simples mapas de chave / valor.

Isso me confundiu. Objetos regulares do JavaScript são dicionários, então como é Mapdiferente de um dicionário? Conceitualmente, eles são idênticos (de acordo com Qual é a diferença entre um Mapa e um Dicionário? )

As referências da documentação chromestatus também não ajudam:

Objetos de mapa são coleções de pares de chave / valor em que as chaves e os valores podem ser valores arbitrários da linguagem ECMAScript. Um valor-chave distinto pode ocorrer apenas em um par de chave / valor na coleção do Mapa. Valores-chave distintos conforme discriminados usando o algoritmo a de comparação selecionado quando o Mapa é criado.

Um objeto Map pode iterar seus elementos na ordem de inserção. O objeto de mapa deve ser implementado usando tabelas de hash ou outros mecanismos que, em média, fornecem tempos de acesso sublineares no número de elementos na coleção. As estruturas de dados usadas nesta especificação de objetos de Mapa destinam-se apenas a descrever a semântica observável necessária dos objetos de Mapa. Não se destina a ser um modelo de implementação viável.

... ainda soa como um objeto para mim, tão claramente eu perdi alguma coisa.

Por que o JavaScript está ganhando um Mapobjeto (com suporte) ? O que isso faz?


Respostas:


286

De acordo com o mozilla:

Um objeto Map pode iterar seus elementos na ordem de inserção - um loop for..of retornará uma matriz de [key, value] para cada iteração.

e

Os objetos são semelhantes aos Mapas, pois ambos permitem definir chaves como valores, recuperar esses valores, excluir chaves e detectar se algo está armazenado em uma chave. Por esse motivo, os objetos têm sido usados ​​como mapas historicamente; no entanto, existem diferenças importantes entre objetos e mapas que melhoram o uso de um mapa.

Um objeto tem um protótipo, portanto, existem chaves padrão no mapa. No entanto, isso pode ser ignorado usando map = Object.create (null). As chaves de um Objeto são Strings, onde podem ter qualquer valor para um Mapa. Você pode obter facilmente o tamanho de um mapa enquanto precisa controlar manualmente o tamanho de um objeto.

Use mapas sobre objetos quando as chaves forem desconhecidas até o tempo de execução e quando todas as chaves forem do mesmo tipo e todos os valores forem do mesmo tipo.

Use objetos quando houver lógica que opere em elementos individuais.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

A iterabilidade em ordem é um recurso que há muito é desejado pelos desenvolvedores, em parte porque garante o mesmo desempenho em todos os navegadores. Então, para mim, isso é grande.

O myMap.has(key)método será especialmente útil e também a myMap.sizepropriedade


13
Uma desvantagem, presumivelmente, é que um mapa requer mais memória (na mesma ordem de magnitude, no entanto) para manter a ordem de inserção.
precisa saber é o seguinte

4
Além de ordenação, os mapas têm outros recursos que foram mencionados aqui (usando qualquer objeto como chave, separação de chaves e acessórios, etc.), mas o FWIW, em alguns casos, a ordem de iteração das propriedades simples do objeto é definida pelo ES2015. Consulte stackoverflow.com/a/32149345 .
JMM

2
Eu não entendi o significado, quando você diz: Um objeto tem um protótipo; portanto, existem chaves padrão no mapa. No entanto, isso pode ser ignorado usandomap = Object.create(null) . O que são chaves padrão? Como as chaves estão relacionadas Object.prototype?
overexchange

4
Meus testes no Chrome mostraram que os mapas não usam mais quantidade significativa de memória para manter a ordem. Acho que havia 0,1KB a mais por um milhão de chaves e não acho que isso tenha sido para manter a ordem. No entanto, esse ~ 0,1KB parece ser uma sobrecarga constante. Se você criar um milhão de mapas com uma tecla e compará-la, é muito maior que o objeto.
Jgmjgm

2
@ luxon você está criando um objeto lá. A especificação ES6 exige que o newoperador seja usado com o Mapsímbolo, ou seja new Map, para criar um objeto de mapa. var a = {}é abreviada para (ou seja, equivalente a)var a = Object.create(Object.prototype)
dudewad

104

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] = truee 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 getObjectIdem 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 hasOwnPropertyacessá-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.


A falta de serialização tem sido uma dor real para muitos desenvolvedores. Veja o voto positivo de Como persisto um mapa ES6 no armazenamento local (ou em outro local)? e Como você JSON.stringify um mapa ES6? .
Franklin Yu

O número está em milissegundos, bytes ou total de objetos?
StefansArya 7/04

Tomou assim ms (algo que tomou é curto para dizer algo usado, portanto, ele gasta tempo neste caso). Embora este seja um teste antigo e eu não tenha mais o código de referência. Provavelmente é muito diferente agora. O problema de exclusão, por exemplo, acredito que foi corrigido.
jgmjgm 8/04

27

Não creio que os pontos a seguir tenham sido mencionados nas respostas até agora, e pensei que valeria a pena mencionar.


Os mapas podem ser maiores

No chrome, posso obter 16,7 milhões de pares de chave / valor com Mapvs. 11,1 milhões com um objeto comum. Quase exatamente 50% mais pares com a Map. Os dois ocupam cerca de 2 GB de memória antes de travar, e acho que isso pode estar relacionado à limitação de memória pelo chrome ( Edit : Sim, tente preencher 2 Mapse você só tem 8,3 milhões de pares cada antes de travar). Você pode testá-lo você mesmo com este código (execute-os separadamente e não ao mesmo tempo, obviamente):

var m = new Map();
var i = 0;
while(1) {
    m.set(((10**30)*Math.random()).toString(36), ((10**30)*Math.random()).toString(36));
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}
// versus:
var m = {};
var i = 0;
while(1) {
    m[((10**30)*Math.random()).toString(36)] = ((10**30)*Math.random()).toString(36);
    i++;
    if(i%1000 === 0) { console.log(i/1000,"thousand") }
}

Os objetos já possuem algumas propriedades / chaves

Este já me tropeçou antes. Objetos regulares têm toString, constructor, valueOf, hasOwnProperty, isPrototypeOfe um monte de outras propriedades pré-existentes. Isso pode não ser um grande problema para a maioria dos casos de uso, mas já causou problemas para mim antes.

Os mapas podem ser mais lentos:

Devido à .getsobrecarga da chamada de função e à falta de otimização interna, o Map pode ser consideravelmente mais lento que um objeto JavaScript antigo comum para algumas tarefas.


1
Na sua opinião, a semântica supera o desempenho aqui? Os mapas soam perfeitos se você precisar de um dicionário, mas é difícil aceitar uma pesquisa mais lenta. Não é uma pesquisa rápida o ponto inteiro dos dicionários?
user2954463

3
Eu definitivamente ir com objetos antigos simples se você está bem com 11 milhões de pares de chave / valor e não se preocupam com as chaves pré-existentes, como toString, constructor, etc. (ou seja, suas chaves são extremamente improvável que colidem com eles). Eles são mais fáceis de trabalhar - por exemplo, o incremento é obj[i] = (obj[i] || 0) + 1, enquanto Mapo map.set(i, (map.get(i) || 0) + 1)que ainda não é ruim, mas mostra como as coisas podem ficar desnecessariamente bagunçadas. Os mapas definitivamente têm seus casos de uso, mas geralmente um objeto simples funciona.

Note que você pode se livrar do padrão toString, constructor(etc.) propriedades do objeto por escrito obj = Object.create(null)em vez de obj = {}.

17

Os objetos podem se comportar como dicionários porque o Javascript é digitado dinamicamente, permitindo adicionar ou remover propriedades em um objeto a qualquer momento.

Mas a nova Map()funcionalidade é muito melhor porque:

  • Ele fornece get, set, has, e deletemétodos.
  • Aceita qualquer tipo para as chaves em vez de apenas cadeias.
  • Fornece um iterador para facilitar o for-ofuso e mantém a ordem dos resultados.
  • Não possui casos de borda com protótipos e outras propriedades aparecendo durante a iteração ou cópia.
  • Ele suporta milhões de itens.
  • É muito rápido e fica cada vez mais rápido à medida que os mecanismos javascript melhoram.

99% do tempo você deve apenas usar a Map(). No entanto, se você estiver usando apenas chaves baseadas em string e precisar de desempenho máximo de leitura, os objetos poderão ser uma escolha melhor.

O detalhe é que (quase todos) os mecanismos javascript compilam objetos até as classes C ++ em segundo plano. Esses tipos são armazenados em cache e reutilizados por seus "contornos"; portanto, quando você cria um novo objeto com as mesmas propriedades exatas, o mecanismo reutiliza uma classe de plano de fundo existente. O caminho de acesso para propriedades nessas classes é muito otimizado e muito mais rápido que a pesquisa de a Map().

Adicionar ou remover uma propriedade faz com que a classe de backup em cache seja recompilada, e é por isso que o uso de um objeto como um dicionário com muitas adições e exclusões de chave é muito lento, mas a leitura e a atribuição de chaves existentes sem alterar o objeto são muito rápidas.

Portanto, se você tiver uma carga de trabalho de gravação pesada e leitura com chaves de seqüência de caracteres, use o objectcomo um dicionário especializado de alto desempenho, mas, para todo o resto, use a Map().


O objeto também fornece get set has deletefuncionalidade etc., mas não é tão elegante (mas também não é ruim). De que maneira é Mapmais fácil usar para iterar? Não tenho certeza se posso concordar.
Andrew

@ Andrew Estou abordando os métodos, e a funcionalidade também é diferente, dependendo do que você está usando e do resultado. A iteração é mais fácil porque o protótipo e as propriedades nativas não aparecem no loop e usam um iterador JS normal que mantém a mesma ordem.
Mani Gandham

11

Além das outras respostas, descobri que o Maps é mais pesado e detalhado para operar do que os objetos.

obj[key] += x
// vs.
map.set(map.get(key) + x)

Isso é importante, porque o código mais curto é mais rápido de ler, mais expressivo e melhor mantido na cabeça do programador .

Outro aspecto: como set () retorna o mapa, não o valor, é impossível encadear atribuições.

foo = obj[key] = x;  // Does what you expect
foo = map.set(key, x)  // foo !== x; foo === map

Depurar mapas também é mais doloroso. Abaixo, você não pode realmente ver quais chaves estão no mapa. Você teria que escrever um código para fazer isso.

Boa sorte na avaliação de um iterador de mapas

Os objetos podem ser avaliados por qualquer IDE:

WebStorm avaliando um objeto


4
Dado tudo isso, parece que o mapa é uma otimização prematura.
PRMan

10

Resumo:

  • Object: Uma estrutura de dados na qual os dados são armazenados como pares de valores-chave. Em um objeto, a chave deve ser um número, sequência ou símbolo. O valor pode ser qualquer coisa, assim como outros objetos, funções etc. Um objeto é uma estrutura de dados não ordenada , ou seja, a sequência de inserção de pares de valores-chave não é lembrada.
  • ES6 Map: Uma estrutura de dados na qual os dados são armazenados como pares de valores-chave. Na qual uma chave exclusiva é mapeada para um valor . A chave e o valor podem estar em qualquer tipo de dados . Um mapa é uma estrutura de dados iterável, isso significa que a sequência de inserção é lembrada e que podemos acessar os elementos em, por exemplo, um for..ofloop

Principais diferenças:

  • UMA Map é ordenado e iterável, enquanto um objeto não é ordenado e não iterável

  • Podemos colocar qualquer tipo de dados como um Map chave, enquanto os objetos podem ter apenas um número, sequência ou símbolo como chave.

  • A Mapherda de Map.prototype. Isso oferece todos os tipos de funções e propriedades utilitárias, o que facilita o trabalho comMap objetos.

Exemplo:

objeto:

let obj = {};

// adding properties to a object
obj.prop1 = 1;
obj[2]    =  2;

// getting nr of properties of the object
console.log(Object.keys(obj).length)

// deleting a property
delete obj[2]

console.log(obj)

Mapa:

const myMap = new Map();

const keyString = 'a string',
    keyObj = {},
    keyFunc = function() {};

// setting the values
myMap.set(keyString, "value associated with 'a string'");
myMap.set(keyObj, 'value associated with keyObj');
myMap.set(keyFunc, 'value associated with keyFunc');

console.log(myMap.size); // 3

// getting the values
console.log(myMap.get(keyString));    // "value associated with 'a string'"
console.log(myMap.get(keyObj));       // "value associated with keyObj"
console.log(myMap.get(keyFunc));      // "value associated with keyFunc"

console.log(myMap.get('a string'));   // "value associated with 'a string'"
                         // because keyString === 'a string'
console.log(myMap.get({}));           // undefined, because keyObj !== {}
console.log(myMap.get(function() {})) // undefined, because keyFunc !== function () {}

fonte: MDN


4

Além de ser iterável em uma ordem bem definida, e a capacidade de usar valores arbitrários como chaves (exceto -0), os mapas podem ser úteis devido aos seguintes motivos:

  • As especificações fazem com que as operações do mapa sejam sublineares em média.

    Qualquer implementação não estúpida de objeto usará uma tabela de hash ou similar, portanto, as pesquisas de propriedades provavelmente serão constantes, em média. Os objetos podem ser ainda mais rápidos que os mapas. Mas isso não é exigido pelas especificações.

  • Objetos podem ter comportamentos inesperados desagradáveis.

    Por exemplo, digamos que você não definiu nenhuma foopropriedade para um objeto recém-criado obj, portanto, espere obj.fooretornar indefinido. Mas foopoderia ser uma propriedade interna herdada Object.prototype. Ou você tenta criar obj.foousando uma atribuição, mas algum setter Object.prototypeé executado em vez de armazenar seu valor.

    Os mapas impedem esse tipo de coisa. Bem, a menos que algum script atrapalhe Map.prototype. E Object.create(null)também funcionaria, mas você perde a sintaxe do inicializador de objetos simples.


4

Quando usar o Google Maps em vez de objetos JavaScript simples?

O objeto JavaScript simples {key: 'value'} contém dados estruturados. Mas o objeto JS simples tem suas limitações:

  1. Somente cadeias e símbolos podem ser usados ​​como chaves de objetos. Se usarmos outras coisas, digamos, números como chaves de um objeto, durante o acesso a essas chaves, veremos que essas chaves serão convertidas em cadeias implicitamente, causando perda de consistência de tipos. nomes de const = {1: 'um', 2: 'dois'}; Object.keys (nomes); // ['1', '2']

  2. Há chances de sobrescrever acidentalmente propriedades herdadas de protótipos, escrevendo identificadores JS como nomes-chave de um objeto (por exemplo, toString, construtor etc.)

  3. Outro objeto não pode ser usado como chave de um objeto; portanto, nenhuma informação extra pode ser gravada para um objeto, escrevendo esse objeto como chave de outro objeto e o valor desse outro objeto conterá as informações extras.

  4. Objetos não são iteradores

  5. O tamanho de um objeto não pode ser determinado diretamente

Essas limitações de objetos são resolvidas pelo Google Maps, mas devemos considerar o Google Maps como complemento para objetos, em vez de substituição. Basicamente, o Mapa é apenas uma matriz de matrizes, mas devemos passar essa matriz para o objeto Map como argumento com nova palavra-chave, caso contrário, somente para a matriz de matrizes, as propriedades e métodos úteis do Mapa não estão disponíveis. E lembre-se de pares de valores-chave dentro da matriz de matrizes ou o Mapa deve ser separado apenas por vírgulas, sem dois pontos como em objetos simples.

3 dicas para decidir se deve usar um mapa ou um objeto:

  1. Use mapas sobre objetos quando as chaves forem desconhecidas até o tempo de execução, porque as chaves formadas pela entrada do usuário ou inconscientemente podem quebrar o código que usa o objeto se essas chaves sobrescreverem as propriedades herdadas do objeto, para que o mapa seja mais seguro nesses casos. Também use mapas quando todas as chaves forem do mesmo tipo e todos os mapas forem do mesmo tipo.

  2. Use mapas se houver necessidade de armazenar valores primitivos como chaves.

  3. Use objetos se precisarmos operar em elementos individuais.

Os benefícios do uso do Maps são:

1. O mapa aceita qualquer tipo de chave e preserva o tipo de chave:

Sabemos que, se a chave do objeto não é uma sequência ou símbolo, o JS a transforma implicitamente em uma sequência. Pelo contrário, o Map aceita qualquer tipo de chave: string, número, booleano, símbolo etc. e o Map preserva o tipo de chave original. Aqui usaremos o número como chave dentro de um mapa e ele permanecerá um número:

const numbersMap= new Map();

numbersMap.set(1, 'one');

numbersMap.set(2, 'two');

const keysOfMap= [...numbersMap.keys()];

console.log(keysOfMap);                        // [1, 2]

Dentro de um mapa, podemos até usar um objeto inteiro como chave. Pode haver momentos em que desejamos armazenar alguns dados relacionados ao objeto, sem anexá-los ao próprio objeto, para que possamos trabalhar com objetos enxutos, mas queremos armazenar algumas informações sobre o objeto. Nesses casos, precisamos usar o Mapa para que possamos transformar Objeto como chave e dados relacionados ao objeto como valor.

const foo= {name: foo};

const bar= {name: bar};

const kindOfMap= [[foo, 'Foo related data'], [bar, 'Bar related data']];

Mas a desvantagem dessa abordagem é a complexidade de acessar o valor por chave, pois precisamos percorrer toda a matriz para obter o valor desejado.

function getBy Key(kindOfMap, key) {
    for (const [k, v]  of kindOfMap) {
        if(key === k) {
            return v;
        }
    }
    return undefined;
}

getByKey(kindOfMap, foo);            // 'Foo related data'

Podemos resolver esse problema de não obter acesso direto ao valor usando um mapa adequado.

const foo= {name: 'foo'};

const bar= {name: 'bar'};

const myMap= new Map();

myMap.set(foo, 'Foo related data');
myMap.set(bar, 'Bar related data');

console.log(myMap.get(foo));            // 'Foo related data'

Poderíamos ter feito isso usando o WeakMap, basta escrever, const myMap = new WeakMap (). As diferenças entre o Map e o WeakMap são que o WeakMap permite a coleta de lixo de chaves (objetos aqui), para evitar vazamentos de memória, o WeakMap aceita apenas objetos como chaves e o WeakMap reduziu o conjunto de métodos.

2. O mapa não tem restrições sobre os nomes das chaves:

Para objetos JS simples, podemos substituir acidentalmente a propriedade herdada do protótipo e isso pode ser perigoso. Aqui, sobrescreveremos a propriedade toString () do objeto ator:

const actor= {
    name: 'Harrison Ford',
    toString: 'Actor: Harrison Ford'
};

Agora vamos definir um fn isPlainObject () para determinar se o argumento fornecido é um objeto simples e esse fn usa o método toString () para verificá-lo:

function isPlainObject(value) {
    return value.toString() === '[object Object]';
}

isPlainObject(actor);        // TypeError : value.toString is not a function

// this is because inside actor object toString property is a string instead of inherited method from prototype

O Mapa não possui restrições quanto aos nomes das chaves, podemos usar nomes de chave como toString, construtor etc. Aqui, embora o objeto actorMap tenha uma propriedade chamada toString, o método toString () herdado do protótipo do objeto actorMap funciona perfeitamente.

const actorMap= new Map();

actorMap.set('name', 'Harrison Ford');

actorMap.set('toString', 'Actor: Harrison Ford');

function isMap(value) {
  return value.toString() === '[object Map]';
}

console.log(isMap(actorMap));     // true

Se tivermos uma situação em que a entrada do usuário cria chaves, devemos levá-las dentro de um mapa, em vez de um objeto simples. Isso ocorre porque o usuário pode escolher um nome de campo personalizado como toString, construtor etc., e esses nomes de chave em um objeto simples podem potencialmente quebrar o código que mais tarde usa esse objeto. Portanto, a solução certa é vincular o estado da interface do usuário a um mapa, não há como quebrar o mapa:

const userCustomFieldsMap= new Map([['color', 'blue'], ['size', 'medium'], ['toString', 'A blue box']]);

3. Mapa é iterável:

Para iterar as propriedades de um objeto simples, precisamos de Object.entries () ou Object.keys (). O Object.entries (plainObject) retorna uma matriz de pares de valores-chave extraídos do objeto, podemos então desestruturar essas chaves e valores e obter saída normal de chaves e valores.

const colorHex= {
  'white': '#FFFFFF',
  'black': '#000000'
}

for(const [color, hex] of Object.entries(colorHex)) {
  console.log(color, hex);
}
//
'white' '#FFFFFF'   
'black' '#000000'

Como o Maps é iterável, é por isso que não precisamos de métodos de entradas () para iterar sobre um mapa e destruir a chave, a matriz de valores pode ser feita diretamente no mapa, pois dentro de um mapa cada elemento vive como uma matriz de pares de valores de chaves separados por vírgulas .

const colorHexMap= new Map();
colorHexMap.set('white', '#FFFFFF');
colorHexMap.set('black', '#000000');


for(const [color, hex] of colorHexMap) {
  console.log(color, hex);
}
//'white' '#FFFFFF'   'black' '#000000'

Também map.keys () retorna um iterador sobre as teclas e map.values ​​() retorna um iterador sobre os valores.

4. Podemos saber facilmente o tamanho de um mapa

Não podemos determinar diretamente o número de propriedades em um objeto simples. Precisamos de um auxiliar fn como, Object.keys () que retorna uma matriz com as chaves do objeto e, usando a propriedade length, podemos obter o número de chaves ou o tamanho do objeto simples.

const exams= {'John Rambo': '80%', 'James Bond': '60%'};

const sizeOfObj= Object.keys(exams).length;

console.log(sizeOfObj);       // 2

Mas no caso do Maps, podemos ter acesso direto ao tamanho do mapa usando a propriedade map.size.

const examsMap= new Map([['John Rambo', '80%'], ['James Bond', '60%']]);

console.log(examsMap.size);

1

Essas duas dicas podem ajudar você a decidir se deseja usar um mapa ou um objeto:

  • Use mapas sobre objetos quando as chaves forem desconhecidas até o tempo de execução e quando todas as chaves forem do mesmo tipo e todos os valores forem do mesmo tipo.

  • Use mapas no caso de haver necessidade de armazenar valores primitivos como chaves, pois o objeto trata cada chave como uma sequência, seja um valor numérico, booleano ou qualquer outro valor primitivo.

  • Use objetos quando houver lógica que opere em elementos individuais.

Fonte: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_Collections#Object_and_Map_compared


2
Essas dicas não parecem particularmente úteis, especialmente porque não é fácil dividir as coisas por esses critérios. Não entendo o primeiro porque os mapas são um benefício quando chaves / valores são do mesmo tipo. Parece mais que está tentando dizer usar objetos como classes / estruturas, mapas como coleções. O segundo está escrito mal, não chegando ao ponto. Significa realmente usar mapas quando você mistura tipos equivalentes de strings ("1" e 1) ou quando precisa / deseja preservar os tipos de chave. O último, eu acho o mesmo que o primeiro, está assumindo que você não sabe o que é um objeto, por isso é vago.
Jgmjgm

1

Esta é uma maneira curta de me lembrar: KOI

  1. Chaves. A chave do objeto é seqüências de caracteres ou símbolos. As chaves do mapa também podem ser números (1 e "1" são diferentes), objetos NaNetc. Eles são usados ===para distinguir as chaves, com uma exceção, NaN !== NaNmas você pode usar NaNcomo chave.
  2. Ordem. A ordem de inserção é lembrada. Então, [...map]ou [...map.keys()]tem uma ordem específica.
  3. Interface. Objeto: obj[key]ou obj.a(em algum idioma []e []=realmente faz parte da interface). Mapa tem get(), set(), has(), delete()etc. Note que você pode usar map[123], mas que está a usá-lo como um objeto JS simples.

0

De acordo com a Mozilla

Objeto vs Mapa em JavaScript de maneira resumida, com exemplos.

Objeto - segue o mesmo conceito do mapa, isto é, usando o par de valores-chave para armazenar dados. Mas há pequenas diferenças que tornam o mapa um melhor desempenho em determinadas situações.

Mapa- é uma estrutura de dados que ajuda a armazenar os dados na forma de pares. O par consiste em uma chave exclusiva e um valor mapeado para a chave. Ajuda a evitar duplicidade.

Principais diferenças

  • O mapa é uma instância de um objeto, mas o vice-versa não é verdadeiro.

var map = new Map();
var obj = new Object(); 
console.log(obj instanceof Map);   // false
console.log(map instanceof Object);  // true

  • Em Objeto, o tipo de dados do campo-chave é restrito a números inteiros, seqüências de caracteres e símbolos. Enquanto no mapa, o campo-chave pode ser de qualquer tipo de dados (número inteiro, uma matriz, um objeto)

var map = new Map();//Empty 
map.set(1,'1');
map.set('one', 1);
map.set('{}', {name:'Hello world'});
map.set(12.3, 12.3)
map.set([12],[12345])

for(let [key,value] of map.entries())
  console.log(key+'---'+value)

  • No mapa, a ordem original dos elementos é preservada. Isso não é verdade no caso de objetos.

let obj ={
  1:'1',
  'one':1,
  '{}': {name:'Hello world'},
  12.3:12.3,
  [12]:[100]
}
console.log(obj)

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.