La diferencia clave es que los Objetos solo admiten claves de cadena, mientras que los Mapas admiten más o menos cualquier tipo de clave.
Si lo hago obj[123] = true
y luego Object.keys(obj)
obtendré en ["123"]
lugar de [123]
. Un mapa preservaría el tipo de clave y devolvería, lo [123]
cual es genial. Los mapas también le permiten usar objetos como claves. Tradicionalmente para hacer esto, tendrías que dar a los objetos algún tipo de identificador único para hacerlos hash (no creo que haya visto algo así como getObjectId
en JS como parte del estándar). Los mapas también garantizan la preservación del orden, por lo que son mucho mejores para la preservación y, a veces, pueden ahorrarle la necesidad de hacer algunos tipos.
Entre mapas y objetos en la práctica hay varios pros y contras. Los objetos obtienen ventajas y desventajas al estar muy estrechamente integrados en el núcleo de JavaScript, lo que los distingue de un mapa significativamente más allá de la diferencia en el soporte clave.
Una ventaja inmediata es que tiene soporte sintáctico para los objetos, lo que facilita el acceso a los elementos. También tiene soporte directo para ello con JSON. Cuando se usa como hash, es molesto obtener un objeto sin ninguna propiedad. De forma predeterminada, si desea utilizar objetos como una tabla hash, se contaminarán y, a menudo, tendrá que recurrir hasOwnProperty
a ellos al acceder a las propiedades. Puede ver aquí cómo, de forma predeterminada, los objetos están contaminados y cómo crear objetos con suerte no contaminados para usar 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
La contaminación en los objetos no es solo algo que hace que el código sea más molesto, más lento, etc., sino que también puede tener consecuencias potenciales para la seguridad.
Los objetos no son tablas hash puras, pero están tratando de hacer más. Tiene dolores de cabeza como hasOwnProperty
no poder obtener la longitud fácilmente ( Object.keys(obj).length
) y así sucesivamente. Los objetos no están destinados a ser utilizados únicamente como mapas hash, sino también como objetos dinámicos extensibles, por lo que cuando los usa como tablas hash puras surgen problemas.
Comparación / Lista de varias operaciones comunes:
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();
Hay algunas otras opciones, enfoques, metodologías, etc., con diferentes altibajos (rendimiento, conciso, portátil, ampliable, etc.). Los objetos son un poco extraños al ser el núcleo del lenguaje, por lo que tiene muchos métodos estáticos para trabajar con ellos.
Además de la ventaja de que Maps preserva los tipos de clave y es capaz de admitir cosas como objetos como claves, están aislados de los efectos secundarios que tienen mucho los objetos. Un mapa es un hash puro, no hay confusión acerca de tratar de ser un objeto al mismo tiempo. Los mapas también se pueden ampliar fácilmente con funciones proxy. Los objetos actualmente tienen una clase de Proxy, sin embargo, el rendimiento y el uso de la memoria son sombríos, de hecho, crear su propio proxy que se parece a Map for Objects actualmente funciona mejor que Proxy.
Una desventaja sustancial para Maps es que no son compatibles con JSON directamente. El análisis es posible pero tiene varias interrupciones:
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;
});
Lo anterior introducirá un golpe de rendimiento serio y tampoco admitirá ninguna tecla de cadena. La codificación JSON es aún más difícil y problemática (este es uno de los muchos enfoques):
// 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())
});
};
Esto no es tan malo si solo está usando Maps, pero tendrá problemas cuando mezcle tipos o use valores no escalares como claves (no es que JSON sea perfecto con ese tipo de problema, la referencia de objeto circular de IE). No lo he probado, pero es probable que perjudique gravemente el rendimiento en comparación con stringify.
Otros lenguajes de secuencias de comandos a menudo no tienen problemas, ya que tienen tipos explícitos no escalares para Map, Object y Array. El desarrollo web a menudo es un problema con los tipos no escalares en los que tiene que lidiar con cosas como PHP combina Array / Map con Object usando A / M para propiedades y JS combina Map / Object con Array extendiendo M / O. Fusionar tipos complejos es la maldición del diablo de los lenguajes de scripting de alto nivel.
Hasta ahora, estos son en gran medida problemas relacionados con la implementación, pero el rendimiento para las operaciones básicas también es importante. El rendimiento también es complejo porque depende del motor y el uso. Realice mis pruebas con un grano de sal, ya que no puedo descartar ningún error (tengo que apresurar esto). También debe ejecutar sus propias pruebas para confirmar, ya que la mía examina solo escenarios simples muy específicos para dar solo una indicación aproximada. Según las pruebas en Chrome para objetos / mapas muy grandes, el rendimiento de los objetos es peor debido a la eliminación, que aparentemente es proporcional al número de teclas en lugar 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
Chrome claramente tiene una gran ventaja al obtener y actualizar, pero el rendimiento de eliminación es horrible. Los mapas usan una pequeña cantidad de memoria en este caso (sobrecarga) pero con solo un Objeto / Mapa siendo probado con millones de claves, el impacto de la sobrecarga para los mapas no se expresa bien. Con la administración de memoria, los objetos también parecen liberarse antes si estoy leyendo el perfil correctamente, lo que podría ser un beneficio a favor de los objetos.
En Firefox para este punto de referencia particular, es una historia 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
Debo señalar de inmediato que en este punto de referencia en particular, eliminar de los objetos en Firefox no está causando ningún problema, sin embargo, en otros puntos de referencia ha causado problemas, especialmente cuando hay muchas claves como en Chrome. Los mapas son claramente superiores en Firefox para grandes colecciones.
Sin embargo, este no es el final de la historia, ¿qué pasa con muchos objetos pequeños o mapas? He hecho un punto de referencia rápido de esto, pero no uno exhaustivo (configuración / obtención) que funciona mejor con un pequeño número de teclas en las operaciones anteriores. Esta prueba trata más sobre memoria e inicialización.
Map Create: 69 // new Map
Object Create: 34 // {}
Una vez más, estas cifras varían, pero básicamente Object tiene una buena ventaja. En algunos casos, la ventaja para Objetos sobre mapas es extrema (~ 10 veces mejor) pero en promedio fue alrededor de 2-3 veces mejor. Parece que los picos de rendimiento extremos pueden funcionar en ambos sentidos. Solo probé esto en Chrome y la creación para perfilar el uso de memoria y los gastos generales. Me sorprendió bastante ver que en Chrome parece que Maps con una tecla usa alrededor de 30 veces más memoria que los Objetos con una tecla.
Para probar muchos objetos pequeños con todas las operaciones anteriores (4 teclas):
Chrome Object Took: 61
Chrome Map Took: 67
Firefox Object Took: 54
Firefox Map Took: 139
En términos de asignación de memoria, estos se comportaron igual en términos de liberación / GC, pero Map utilizó 5 veces más memoria. Esta prueba usó 4 teclas donde, como en la última prueba, solo configuré una tecla para que esto explicara la reducción en la sobrecarga de memoria. Ejecuté esta prueba varias veces y Map / Object son más o menos compactos en general para Chrome en términos de velocidad general. En Firefox para objetos pequeños, existe una clara ventaja de rendimiento sobre los mapas en general.
Por supuesto, esto no incluye las opciones individuales que pueden variar enormemente. No recomendaría micro-optimización con estas cifras. Lo que puede sacar de esto es que, como regla general, considere Maps con más fuerza para los almacenes de valores clave muy grandes y los objetos para almacenes de valores clave pequeños.
Más allá de eso, la mejor estrategia con estos dos es implementarlo y hacer que funcione primero. Al crear un perfil, es importante tener en cuenta que a veces las cosas que no pensarías que serían lentas al mirarlas pueden ser increíblemente lentas debido a las peculiaridades del motor como se ve con el caso de eliminación de clave de objeto.