Acabo de descubrir chromestatus.com y, después de perder varias horas de mi día, encontré esta entrada de características :
Mapa: Los objetos de mapa son mapas simples de clave / valor.
Eso me confundio. Los objetos JavaScript normales son diccionarios, entonces, ¿en qué se Map
diferencia de un diccionario? Conceptualmente, son idénticos (de acuerdo con ¿Cuál es la diferencia entre un Mapa y un Diccionario? )
Las referencias de documentación chromestatus tampoco ayudan:
Los objetos de mapa son colecciones de pares clave / valor donde las claves y los valores pueden ser valores de lenguaje ECMAScript arbitrarios. Un valor clave distinto solo puede aparecer en un par clave / valor dentro de la colección del Mapa. Distintos valores clave como discriminados utilizando el algoritmo de comparación que se selecciona cuando se crea el Mapa.
Un objeto Map puede iterar sus elementos en orden de inserción. El objeto de mapa debe implementarse utilizando tablas hash u otros mecanismos que, en promedio, proporcionan tiempos de acceso que son sublineales en la cantidad de elementos en la colección. Las estructuras de datos utilizadas en esta especificación de objetos de Mapa solo tienen la intención de describir la semántica observable requerida de los objetos de Mapa. No pretende ser un modelo de implementación viable.
... todavía me suena como un objeto, así que claramente me he perdido algo.
¿Por qué JavaScript está ganando un objeto (bien soportado) Map
? ¿Qué hace?
Respuestas:
De acuerdo con Mozilla:
y
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
La iterabilidad en orden es una característica que los desarrolladores siempre han deseado, en parte porque garantiza el mismo rendimiento en todos los navegadores. Entonces para mí eso es grande.
El
myMap.has(key)
método será especialmente útil, y también lamyMap.size
propiedad.fuente
map = Object.create(null)
. ¿Qué son las claves predeterminadas? ¿Cómo se relacionan las clavesObject.prototype
?new
operador se use con elMap
símbolo, es decirnew Map
, para crear un objeto de mapa.var a = {}
es la abreviatura de (significado equivalente a)var a = Object.create(Object.prototype)
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 luegoObject.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í comogetObjectId
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: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:
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:
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):
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):
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:
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.
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):
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.
fuente
No creo que se hayan mencionado los siguientes puntos en las respuestas hasta ahora, y pensé que valdría la pena mencionarlos.
Los mapas pueden ser más grandes
En Chrome puedo obtener 16,7 millones de pares clave / valor con
Map
vs. 11,1 millones con un objeto regular. Casi exactamente un 50% más de pares con aMap
. Ambos ocupan aproximadamente 2 GB de memoria antes de bloquearse, por lo que creo que puede tener que ver con la limitación de memoria por cromo ( Editar : Sí, intente llenar 2Maps
y solo obtendrá 8.3 millones de pares antes de que se bloquee). Puede probarlo usted mismo con este código (ejecútelos por separado y no al mismo tiempo, obviamente):Los objetos ya tienen algunas propiedades / claves
Este me ha hecho tropezar antes. Objetos normales tienen
toString
,constructor
,valueOf
,hasOwnProperty
,isPrototypeOf
y un montón de otras propiedades preexistentes. Esto puede no ser un gran problema para la mayoría de los casos de uso, pero antes me ha causado problemas.Los mapas pueden ser más lentos:
Debido a la
.get
sobrecarga de llamadas de función y la falta de optimización interna, Map puede ser considerablemente más lento que un objeto JavaScript antiguo para algunas tareas.fuente
toString
,constructor
, etc. (es decir, las teclas son muy poco probable que chocan con ellos). Es más fácil trabajar con ellos, por ejemplo, el incremento lo esobj[i] = (obj[i] || 0) + 1
, mientras que conMap
esomap.set(i, (map.get(i) || 0) + 1)
todavía no es tan malo, pero solo muestra cómo las cosas pueden complicarse innecesariamente. Los mapas definitivamente tienen sus casos de uso, pero a menudo un objeto simple servirá.toString
,constructor
, (etc.) las propiedades del objeto de escrituraobj = Object.create(null)
en lugar deobj = {}
.Los objetos pueden comportarse como diccionarios porque Javascript se escribe dinámicamente, lo que le permite agregar o eliminar propiedades en un objeto en cualquier momento.
Pero la nueva
Map()
funcionalidad es mucho mejor porque:get
,set
,has
, ydelete
métodos.for-of
uso fácil y mantiene el orden de los resultados.El 99% del tiempo solo debes usar a
Map()
. Sin embargo, si solo usa claves basadas en cadenas y necesita un rendimiento de lectura máximo, los objetos pueden ser una mejor opción.El detalle es que (casi todos) los motores javascript compilan objetos en clases de C ++ en segundo plano. Estos tipos se almacenan en caché y se reutilizan por su "esquema", por lo que cuando crea un nuevo objeto con las mismas propiedades exactas, el motor reutilizará una clase de fondo existente. La ruta de acceso para las propiedades en estas clases está muy optimizada y es mucho más rápida que la búsqueda de a
Map()
.Agregar o quitar una propiedad hace que la clase de respaldo en caché se vuelva a compilar, por lo que usar un objeto como diccionario con muchas adiciones y eliminaciones de claves es muy lento, pero las lecturas y la asignación de claves existentes sin cambiar el objeto son muy rápidas.
Por lo tanto, si tiene una carga de trabajo de lectura pesada de escritura única con teclas de cadena, use un
object
como diccionario especializado de alto rendimiento, pero para todo lo demás use aMap()
.fuente
get set has delete
funcionalidad, etc., no es tan elegante (pero tampoco está mal). ¿De qué manera esMap
más fácil de usar para iterar? No estoy seguro de poder estar de acuerdo.Además de las otras respuestas, descubrí que los mapas son más difíciles de manejar y detallados para operar que los objetos.
Esto es importante, porque el código más corto es más rápido de leer, más expresivo directamente y se mantiene mejor en la cabeza del programador .
Otro aspecto: porque set () devuelve el mapa, no el valor, es imposible encadenar las asignaciones.
La depuración de mapas también es más dolorosa. A continuación, no puede ver qué teclas hay en el mapa. Tendrías que escribir código para hacer eso.
Los objetos pueden ser evaluados por cualquier IDE:
fuente
Resumen:
Object
: Una estructura de datos en la que los datos se almacenan como pares de valores clave. En un objeto, la clave debe ser un número, una cadena o un símbolo. El valor puede ser cualquier cosa, también otros objetos, funciones, etc. Un objeto es una estructura de datos no ordenada , es decir, no se recuerda la secuencia de inserción de pares de valores claveES6 Map
: Una estructura de datos en la que los datos se almacenan como pares de valores clave. En el que una clave única se asigna a un valor . Tanto la clave como el valor pueden estar en cualquier tipo de datos . Un mapa es una estructura de datos iterable, esto significa que se recuerda la secuencia de inserción y que podemos acceder a los elementos, por ejemplo, en unfor..of
bucleDiferencias clave
A
Map
es ordenado e iterable, mientras que un objeto no está ordenado ni es iterablePodemos poner cualquier tipo de datos como
Map
clave, mientras que los objetos solo pueden tener un número, una cadena o un símbolo como clave.A
Map
hereda deMap.prototype
. Esto ofrece todo tipo de funciones y propiedades de utilidad que hacen que trabajar conMap
objetos sea mucho más fácil.Ejemplo:
objeto:
Mapa:
fuente: MDN
fuente
Además de ser iterables en un orden bien definido, y la capacidad de usar valores arbitrarios como claves (excepto
-0
), los mapas pueden ser útiles por las siguientes razones:La especificación exige que las operaciones de mapa sean sublineales en promedio.
Cualquier implementación no estúpida del objeto usará una tabla hash o similar, por lo que las búsquedas de propiedades probablemente serán constantes en promedio. Entonces los objetos podrían ser incluso más rápidos que los mapas. Pero eso no es requerido por la especificación.
Los objetos pueden tener comportamientos inesperados desagradables.
Por ejemplo, supongamos que no configuró ninguna
foo
propiedad en un objeto recién creadoobj
, por lo que esperaobj.foo
volver indefinido. Perofoo
podría ser propiedad incorporada heredada deObject.prototype
. O intentas crearobj.foo
usando una tarea, pero algún setter enObject.prototype
carreras lugar de almacenar su valor.Los mapas evitan este tipo de cosas. Bueno, a menos que algún script se equivoque
Map.prototype
. YObject.create(null)
funcionaría también, pero luego se pierde la sintaxis de inicializador de objeto simple.fuente
¿Cuándo usar Maps en lugar de objetos JavaScript simples?
El simple objeto JavaScript {key: 'value'} contiene datos estructurados. Pero el objeto JS simple tiene sus limitaciones:
Solo se pueden usar cadenas y símbolos como claves de objetos. Si usamos cualquier otra cosa, digamos, los números como claves de un objeto, entonces al acceder a esas claves veremos que esas claves se convertirán en cadenas implícitamente, lo que nos hará perder la consistencia de los tipos. const names = {1: 'uno', 2: 'dos'}; Object.keys (nombres); // ['1', '2']
Hay posibilidades de sobrescribir accidentalmente las propiedades heredadas de los prototipos escribiendo identificadores JS como nombres clave de un objeto (por ejemplo, toString, constructor, etc.)
No se puede usar otro objeto como clave de un objeto, por lo que no se puede escribir información adicional para un objeto escribiendo ese objeto como clave de otro objeto y el valor de ese otro objeto contendrá la información adicional
Los objetos no son iteradores
El tamaño de un objeto no se puede determinar directamente
Maps resuelve estas limitaciones de los objetos, pero debemos considerar Maps como complemento para los objetos en lugar de reemplazarlos. Básicamente, Map es solo una matriz de matrices, pero debemos pasar esa matriz de matrices al objeto Map como argumento con una nueva palabra clave; de lo contrario, solo para la matriz de matrices, las propiedades y métodos útiles de Map no están disponibles. Y recuerde que los pares clave-valor dentro de la matriz de matrices o el Mapa deben estar separados solo por comas, sin dos puntos como en los objetos simples.
3 consejos para decidir si usar un Mapa o un Objeto:
Use mapas sobre objetos cuando las claves son desconocidas hasta el tiempo de ejecución porque las claves formadas por la entrada del usuario o sin saberlo pueden romper el código que usa el objeto si esas claves sobrescriben las propiedades heredadas del objeto, por lo que el mapa es más seguro en esos casos. También use mapas cuando todas las teclas sean del mismo tipo y todos los mapas sean del mismo tipo.
Utilice mapas si es necesario almacenar valores primitivos como claves.
Use objetos si necesitamos operar sobre elementos individuales.
Los beneficios de usar Maps son:
1. Map acepta cualquier tipo de clave y conserva el tipo de clave:
Sabemos que si la clave del objeto no es una cadena o símbolo, JS la transforma implícitamente en una cadena. Por el contrario, Map acepta cualquier tipo de claves: cadena, número, booleano, símbolo, etc. y Map conserva el tipo de clave original. Aquí usaremos número como clave dentro de un Mapa y seguirá siendo un número:
Dentro de un mapa, incluso podemos usar un objeto completo como clave. Puede haber ocasiones en las que queramos almacenar algunos datos relacionados con el objeto, sin adjuntar estos datos dentro del objeto en sí, de modo que podamos trabajar con objetos lean pero queramos almacenar cierta información sobre el objeto. En esos casos, necesitamos usar Map para poder hacer Object como clave y los datos relacionados del objeto como valor.
Pero la desventaja de este enfoque es la complejidad de acceder al valor por clave, ya que tenemos que recorrer toda la matriz para obtener el valor deseado.
Podemos resolver este problema de no obtener acceso directo al valor utilizando un Mapa adecuado.
Podríamos haber hecho esto usando WeakMap, solo tenemos que escribir, const myMap = new WeakMap (). Las diferencias entre Map y WeakMap son que WeakMap permite la recolección de basura de claves (aquí objetos) para evitar fugas de memoria, WeakMap acepta solo objetos como claves y WeakMap ha reducido el conjunto de métodos.
2. El mapa no tiene restricción sobre los nombres clave:
Para objetos JS simples, podemos sobrescribir accidentalmente la propiedad heredada del prototipo y puede ser peligroso. Aquí sobrescribiremos la propiedad toString () del objeto actor:
Ahora definamos un fn isPlainObject () para determinar si el argumento proporcionado es un objeto simple y este fn usa el método toString () para verificarlo:
El mapa no tiene ninguna restricción en los nombres de clave, podemos usar nombres de clave como toString, constructor, etc. Aquí, aunque el objeto actorMap tiene una propiedad llamada toString pero el método toString () heredado del prototipo del objeto actorMap funciona perfectamente.
Si tenemos una situación en la que la entrada del usuario crea claves, debemos tomar esas claves dentro de un Mapa en lugar de un objeto plano. Esto se debe a que el usuario puede elegir un nombre de campo personalizado como, toString, constructor, etc., entonces dichos nombres de clave en un objeto plano pueden potencialmente romper el código que luego usa este objeto. Entonces, la solución correcta es vincular el estado de la interfaz de usuario a un mapa, no hay forma de romper el Mapa:
3. El mapa es iterable:
Para iterar las propiedades de un objeto plano necesitamos Object.entries () u Object.keys (). Object.entries (plainObject) devuelve una matriz de pares de valores clave extraídos del objeto, luego podemos desestructurar esas claves y valores y podemos obtener claves y valores normales.
Como los mapas son iterables, es por eso que no necesitamos métodos de entradas () para iterar sobre un mapa y la desestructuración de la clave, la matriz de valores se puede hacer directamente en el mapa, ya que dentro de un mapa cada elemento vive como una matriz de pares de valores clave separados por comas .
También map.keys () devuelve un iterador sobre las claves y map.values () devuelve un iterador sobre los valores.
4. Podemos saber fácilmente el tamaño de un mapa
No podemos determinar directamente el número de propiedades en un objeto plano. Necesitamos un ayudante fn como, Object.keys () que devuelve una matriz con las claves del objeto y luego, usando la propiedad de longitud, podemos obtener el número de claves o el tamaño del objeto plano.
Pero en el caso de Mapas, podemos tener acceso directo al tamaño del Mapa usando la propiedad map.size.
fuente
Estos dos consejos pueden ayudarlo a decidir si usar un Mapa o un Objeto:
Use mapas sobre objetos cuando las claves son desconocidas hasta el tiempo de ejecución, y cuando todas las claves son del mismo tipo y todos los valores son del mismo tipo.
Use mapas en caso de que sea necesario almacenar valores primitivos como claves porque el objeto trata cada clave como una cadena, ya sea un valor numérico, un valor booleano o cualquier otro valor primitivo.
Use objetos cuando haya lógica que opere en elementos individuales.
Fuente: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Keyed_Collections#Object_and_Map_compared
fuente
Este es un camino corto para recordarlo: KOI
NaN
, etc. Se usa===
para distinguir entre teclas, con una excepción,NaN !== NaN
pero puede usarNaN
como una tecla.[...map]
o[...map.keys()]
tiene un orden particular.obj[key]
oobj.a
(en algún idioma,[]
y[]=
son realmente parte de la interfaz). Mapa tieneget()
,set()
,has()
,delete()
etc. Nota que se puede utilizarmap[123]
, pero que lo está utilizando como un objeto JS llanura.fuente
De acuerdo con Mozilla
Objeto vs Mapa en JavaScript de forma breve con ejemplos.
Objeto: sigue el mismo concepto que el de mapa, es decir, usar un par clave-valor para almacenar datos. Pero existen pequeñas diferencias que hacen que el mapa tenga un mejor desempeño en ciertas situaciones.
Map- es una estructura de datos que ayuda a almacenar los datos en forma de pares. El par consta de una clave única y un valor asignado a la clave. Ayuda a prevenir la duplicidad.
Diferencias clave
fuente