This is the fifth day of my participation in the August More text Challenge. For details, see:August is more challenging

Prior to ECMAScript 6, implementing “key/value” storage in JavaScript was easier and more efficient using objects, using Object properties as keys and using properties to reference values.

However, this implementation is not without problems. Object can only use numbers, strings, or symbols as keys, so when an Object is used as a key, Object casts the Object into a string (calling its toString) and uses it as a key. For example, Object => [Object Object], This makes it impossible to use different objects to map different values.

Map, a new collection type added to ES6, is a true key/value storage mechanism. Most of the features of Map can be implemented using the Object type, but there are some subtle differences. Which one is used in concrete practice, it is still worth distinguishing carefully.

Basic API

Map provides a series of initialization, manipulation, and traversal methods to facilitate our use.

Initialize the

Map is a constructor that requires new to create the example

let map = new Map(a);//Map(0) {}
Copy the code

If you want to initialize the instance as it is created, you can pass an iterable to the Map constructor that contains an array of key/value pairs. Each key/value pair in the iterable is inserted into the instance in the order of iteration:

// Initialize the mapping using nested arrays
const m1 = new Map([["key1"."val1"], 
 ["key2"."val2"], 
 ["key3"."val3"]]); >>>Map(3) {"key1"= >"val1"."key2"= >"val2"."key3"= >"val3"}


// Initialize the mapping using custom iterators
const m2 = new Map({[Symbol.iterator]: function* () { 
 yield ["key1"."val1"]; 
 yield ["key2"."val2"]; 
 yield ["key3"."val3"]; }}); >>>Map(3) {"key1"= >"val1"."key2"= >"val2"."key3"= >"val3"}
Copy the code

operation

Map operations are set, GET, HAS, DELETE, and clear

size

An internal property of a map object that returns the number of elements. This property is read-only, so you cannot change the size of an array by changing this value. Change size with the set method to return undefined

let myMap = new Map(a); myMap.set("a"."alpha");
myMap.set("b"."beta");
myMap.set("g"."gamma");

myMap.size / / 3
Copy the code

set

Adds or updates a (new) key-value pair that specifies key and value and returns itself, thus chaining calls.

let myMap = new Map(a);// Add a new element to the Map
myMap.set("bar"."foo");
myMap.set(1."foobar");

// Update the value of an element in the Map object
myMap.set("bar"."baz");

// String calls
myMap.set('bar'.'foo')
     .set(1.'foobar')
     .set(2.'baz');
Copy the code

get

Returns the specified element in the Map.

let myMap = new Map(a); myMap.set("bar"."foo");

myMap.get("bar");  / / return "foo"
myMap.get("baz");  / / returns undefined
Copy the code

has

Returns a bool indicating whether the specified element is present in the map.

let myMap = new Map(a); myMap.set("bar"."foo");

myMap.has("bar");  // returns true
myMap.has("baz");  // returns false
Copy the code

delete

Removes the specified element from the Map object.

let myMap = new Map(a); myMap.set("bar"."foo");

myMap.delete("bar"); // Returns true. The element was successfully removed
myMap.has("bar");    // Return false." The bar" element will no longer exist in the Map instance
Copy the code

clear

Removes all elements from a Map object. The return value is undefined

let myMap = new Map(a); myMap.set("bar"."baz");
myMap.set(1."foo");

myMap.size;       / / 2
myMap.has("bar"); // true

myMap.clear();

myMap.size;       / / 0
myMap.has("bar")  // false
Copy the code

traverse

Map provides methods to return iterator objects, making it easy to use for… Of and forEach are traversed

keys

Returns a referenced Iterator object. It contains the key values that are inserted in order for each element in the Map object.

let myMap = new Map(a); myMap.set("0"."foo");
myMap.set(1."bar");
myMap.set({}, "baz");

let mapIter = myMap.keys();

console.log(mapIter.next().value); / / "0"
console.log(mapIter.next().value); / / 1
console.log(mapIter.next().value); // Object

for(let i of mapIter)
    console.log(i)
Copy the code

values

Return a new Iterator. It contains the value values for each element inserted in the Map object in order.

let myMap = new Map(a); myMap.set("0"."foo");
myMap.set(1."bar");
myMap.set({}, "baz");

let mapIter = myMap.values();

console.log(mapIter.next().value); // "foo"
console.log(mapIter.next().value); // "bar"
console.log(mapIter.next().value); // "baz"
Copy the code

entires

Return a new Iterator containing pairs of [key, value] in the same order as the Map was inserted.

const m = new Map([["key1"."val1"], 
 ["key2"."val2"], 
 ["key3"."val3"]]);for (let pair of m.entries()) { 
 console.log(pair); 
} 
// [key1,val1] 
// [key2,val2] 
// [key3,val3]

m.forEach((val, key) = > console.log(`${key} -> ${val}`)); 
// key1 -> val1 
// key2 -> val2 
// key3 -> val3
Copy the code

And object

The type of the key

The key of a Map can be any value, including a function, an object, or any basic type. The key of an Object can only be a String or a Symbol. When an Object uses a type other than a String as a key, it is internally converted to a String and used as a key.

const div1 = document.getElementById('div1')
const div2 = document.getElementById('div2')
const obj = {}
const map = new Map(a)// When the DOM node Object is a key value of Object, it is converted to a string (called toString), with the value [Object HTMLDivElement] as the key
// So div1 is overwritten by div2
obj[div1] = 'div1'
obj[div2] = 'div2'

console.log(obj)//{ [object HTMLDivElement]: "div2" }

// Map directly uses dom nodes as keys
map.set(div1,"div1")
map.set(div2,"div2")

console.log(map)//{ div#div1 => "div1", div#div2 => "div2" }
Copy the code

The order of the keys

Keys in a Map are ordered. Thus, when iterating, a Map object returns the keys in the order in which they were inserted. The key of Object is unordered

Since the ECMAScript 2015 specification, objects do retain the order in which strings and Symbol keys are created; Therefore, iterating over an object that only has string keys produces keys in insertion order. —MDN

[] operator

Both Map and Object can use the [] operator, but the effect is different.

const map = new Map(a)const obj = new Object()

map['name'] ="jalenl" //Map(1){name:"Jalen"}
map.has('name')//false
map.get('name')//undefined

map.set('name'.'jalenl')// Map(1){"name" => "Jalen"}
map.has('name')//true

obj['name'] ="jalenl" //{name:"Jalen"}
Copy the code

Map and Object use [] to modify their own attributes, but in the case of Map, their attributes have nothing to do with the elements, and size() yields the same number of elements.

But Map can use the [] operator because the lowest level of its prototype chain is Object, which it inherits from.

const map = new Map()
map instanceof Object//true
Copy the code

The built-in key

Map does not contain any keys by default. Contains only explicitly inserted keys. An Object instance has a stereotype. The key names on the stereotype chain may conflict with the custom key names.

ES5 can use object.create (null) to create an Object without a stereotype.

The iterator

Map has built-in iterators. Its default iterator is Entries (). Object has no built-in iterator. Therefore the for… Of can be used directly for map instances, but cannot be used for object instances. You must set iterators for object instances.

String, Array, TypedArray, Map, and Set are all built-in iterables because their prototype objects have a Symbol. Iterator method.

const obj ={
  name:"jalenl".age: 18
}
const map = new Map([["name"."jalenl"], ["age"."18"]])

console.log(map[Symbol.iterator])//[Function: entries]
console.log(obj[Symbol.iterator])//undefined
Copy the code

How to choose

For common development tasks, choosing Object or Map is a matter of personal preference, but there are significant differences in memory management and performance.

  1. Memory footprint

The engineering level implementations of Object and Map vary significantly from browser to browser, but the amount of memory used to store a single key/value pair increases linearly with the number of keys. Adding or removing key/value pairs in batches depends on the project implementation of each browser for that type of memory allocation. This varies from browser to browser, but given a fixed size of memory, a Map can store approximately 50% more key/value pairs than an Object.

  1. Insert performance

The cost of inserting a new key/value pair into an Object and a Map is roughly equal, although inserting a Map is generally slightly faster in all browsers. For both types, the insertion speed does not increase linearly with the number of key/value pairs. If your code involves a lot of insert operations, then clearly Map performs better.

  1. To find the speed

Unlike insertion, the performance difference between finding key/value pairs from large objects and maps is minimal, but objects are sometimes faster if they contain only a small number of key/value pairs. In cases where objects are used as arrays (such as using contiguous integers as attributes), the viewer engine can be optimized to use a more efficient layout in memory. This is not possible for Map. For both types, the search speed does not increase linearly as the number of key/value pairs increases. If your code involves a lot of look-ups, it may be better to choose Object in some cases.

  1. Delete the performance

The performance of deleting Object properties using DELETE has long been criticized and still is in many browsers. To this end, some pseudo-deletion of object properties occurs, including setting the property value to undefined or NULL. But a lot of the time, this is a nasty or inappropriate compromise. For most browser engines, Map delete() is faster than insert and find. If your code involves a lot of deletions, then Map is the obvious choice.