It is well known that the two data structures Object
and Map
in JavaScript
are very similar.
However, if we look deeper into the underlying principles, there are still a lot of differences between them, and comparing them can help us better understand their uses and scenarios.
Key type
Object
The key of Object
must be of type String
or Symbol
, and the toString
method is called by default to convert the key to type String
, so there may be [a key overwrite problem](# key uniqueness).
Note:
Array
andFunction
are essentially inheritance ofObject
, so both have correspondingtoString
methods.
Object keys
The Object.toString
method is called when an object is used as a key to convert it to an object string ("[object Object]"
).
({}.toString()); // "[object Object]"
var obj = {};
obj[{}] = "ok";
console.log(JSON.stringify(obj)); // {"[object Object]": "ok"}
Array Keys
The Array.toString
method is called when using an array as a key to convert it to an empty string (""
).
[].toString(); // ""
var obj = {};
obj[[]] = "ok";
console.log(JSON.stringify(obj)); // {"": "ok"}
Function Keys
Converting a function to a function string ("() => {}"
) is done by calling the Function.toString
method when the function is used as a key.
(function test() => {}).toString(); // "() => {}"
var obj = {};
obj[() => {}] = "ok";
console.log(JSON.stringify(obj)); // {"() => {}": "ok"}
Map
Map
supports arbitrary types of keys.
Map objects are collections of key/value pairs where both the keys and values may be arbitrary ECMAScript language values.
Key uniqueness
Object key override with the same name
Since the toString
method is called by default for Object
keys, multiple assignments will be overwritten if the current key is an empty object ({}
) or an empty array ([]
).
var obj = {};
obj[{}] = "step1";
obj[{}] = "step2";
console.log(JSON.stringify(obj)); // {"[object Object]": "step2"}
Map unique keys
Each key in Map
is unique, and in the procedure Map
compares the type
or reference
of the stored key. Suppose the current Map
has two empty objects ({}
) stored in it, both of the same type, but referenced at different memory addresses in the Stack
, then the Map
will determine that they are two separate keys, as in the following example.
Traversal order
Object unordered
The result of traversing Object
is an unordered list.
var obj = { 1: 1, 2: 2, a: "a", f: "f" };
console.log(Object.keys(obj)); // ["1", "2", "a", "f"]
obj = { a: "a", 1: 1, 2: 2, f: "f" };
console.log(Object.keys(obj)); // ["1", "2", "a", "f"]
Map ordered
The result after traversing Map
is an ordered list.
var map = new Map();
map.set(1, 1);
map.set("a", "a");
map.set(2, 2);
console.log([... .map.values()]); // [1, "a", 2]
Traversable
Object
Object
does not implement an iterator (@@iterator
) interface and cannot be traversed using for of
, but can be traversed using methods such as for in
. Of course, Object
does not support it natively but can extend @@iterator
to implement iterators, see iterator for details.
var obj = { a: "a", 1: 1, 2: 2, f: "f" };
for (key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(key);
}
}
console.log(Object.keys(obj)); // ["1", "2", "a", "f"]
Map
Map
internally implements the traverser (@@iterator
) interface, which can be traversed using for of
.
Map.prototype [ @@iterator ] ( )
var map = new Map();
map.set(1, 1);
map.set("a", "a");
map.set(2, 2);
for (item of map) {
console.log(item);
}
Inheritance Relationships
From the prototype chain inheritance structure, we can see that Map
actually inherits from Object
at the bottom, i.e. Map
is an instance object of Object
, and conversely Object
is not an instance object of Map
.