ES6+ Map
1. 前言
在 ES5 中使用 Object
来存储对象,然而这种存储存在一些问题,比如说 Object
中的键是无序的,Object
的键只能是字符串类型等等。ES6 提供了 Map
数据结构,保存的是键值对,而且能够记住键的插入顺序,并且任何值 (对象或者原始值) 都可以作为一个键或一个值,这样极大地扩展了数据存储的场景。
2. Map 使用详情
2.1 Map 基本说明
var map = Map([iterable]);
Map
对象在插入数据时是按照顺序来插入的,也就是说在插入时会保持插入的位置,Object
的在插入数据时没有顺序概念。Map
对象可以被 for...of
循环,在每次迭代后会返回一个形式为 [key,value]
的数组,这个我们在下面的例子中会说到。
Map
的本质其实还是一个对象,并且它也是继承 Object 的,看下面的实例:
var map = new Map([["x", ], ["y", ]]);
console.log(map instanceof Object); //true
从上面的代码中可以看出 Object 在实例 map 的原型链上。
在初始化 Map 对象时,如果默认参数的数组中超过两个以上的值不会被 Map
对象读取。
var map = new Map([["x", , 'a', 'b'], ["y", , 'c'], ["z", , 'd']]);
console.log(map) // Map(3) {"x" => 1, "y" => 2, "z" => 3}
var map = new Map([["x", ], ["y", ], ["z", ]]);
console.log(map.size) // 3
myMap.set(key, value);
var map = new Map();
var str = 'string';
var obj = {};
var arr = [];
var fun = function() {};
map.set(str, '键的类型字符串');
map.set(obj, '键的类型对象');
map.set(arr, '键的类型数组');
map.set(fun, '键的类型函数');
var map = new Map();
map.set('a', ).set('b', ).set('c', );
console.log(map); // Map(3) {"a" => 1, "b" => 2, "c" => 3}
myMap.get(key);
console.log(map.get('string')); // "键的类型字符串"
console.log(map.get(str)); // "键的类型字符串"
console.log(map.get(obj)); // "键的类型对象"
console.log(map.get(arr)); // "键的类型数组"
console.log(map.get(fun)); // "键的类型数组"
myMap.has(key);
实例:
var map = new Map();
map.set("a", );
map.has("a"); // true
map.has("b"); // false
delete()
方法用于移除 Map
实例上的指定元素,如果 Map
对象中存在该元素,则移除它并返回 true
;否则如果该元素不存在则返回 false
。
myMap.delete(key);
实例:
var map = new Map();
map.set("a", );
map.delete("a"); // true
map.has("a"); // false
clear()
方法会移除 Map 对象中的所有元素,返回 undefined
。
myMap.clear(key);
实例:
var map = new Map();
map.set("a", );
map.clear(); // 返回 undefined
这里需要注意的是 clear()
返回的值是 undefined
而不是 true
所以如果在判断结果的时候需要注意这一点。
2.3 Map 的扩展方法
myMap.keys();
var map = new Map();
map.set('a', );
map.set('b', );
map.set('c', );
var keys = map.keys()
console.log(keys.next().value); // "a"
console.log(keys.next().value); // "b"
console.log(keys.next().value); // "c"
myMap.values();
实例:
var map = new Map();
map.set('a', );
map.set('b', );
map.set('c', );
var values = map.values()
console.log(values.next().value); // 1
console.log(values.next().value); // 2
console.log(values.next().value); // 3
myMap.entries()
实例:
var map = new Map();
map.set('a', );
map.set('b', );
map.set('c', );
var values = map.values()
console.log(values.next().value); // 1
console.log(values.next().value); // 2
console.log(values.next().value); // 3
keys()
、values()
、entries()
都可以被 for...of
循环。
var map = new Map([["x", ], ["y", ], ["z", ]]);
for (let value of map.values()) {
console.log(value);
}
// 1
// 2
// 3
for (let [key, value] of map.entries()) {
console.log(key + " = " + value);
}
// x = 1
// y = 2
// z = 3
注意在循环 entries()
结果的时候,因为每一项是包含键值的数组,可以通过 [key, value]
这种数组结构的方式把键值结构出来直接使用。
3. Map 和 Object
Map
和 Object
有非常多的相似的地方,Map
的出现也是为了弥补 Object
的不足。 Object
的键只能是字符串,Map
的键可以是任意类型的值(包括对象),所以 Map
是一种更完善的 Hash 结构实现。
3.1 键无序问题
Object 的键是无序的,当键可以隐式转换为数值时,在循环的时候就会被优先排序。这也是为什么要求最好不要使用 Number 类型作为对象的属性。
var obj = {
c: 'C',
: ,
a: 'A',
: ,
}
for (let key in obj) {
console.log(key, obj[key])
}
// 1 1
// 3 3
// c C
// a A
Map
会记录插入的顺序,存放的是键值对的组合,并且不会做类型转换。Map
可以用 forEach 循环。
var map = new Map();
map.set('c', 'C').set(, ).set('a', 'A').set(, );
map.forEach((item, key) => {
console.log(key, item, typeof key)
})
// c C string
// 3 3 "number"
// a A string
// 1 1 "number"
从上面的代码中,使用 typeof 去检查 key 的数据类型,可以看出 Map
并不会对键做类型转换。
3.2 应用场景
var obj = {
id: ,
desc: "imooc ES6 wiki",
print: function(){
console.log(this.desc)
}
}
console.log(obj.print()); //"imooc ES6 wiki"
所以,尽管 Map 相对于 Object 有很多优点,但 Object 在某些场景更易于使用,比如上面的实例。毕竟 Object 是 JavaScript 中最基础的概念,给出使用场景的几个参考。
使用 Object 的场景:
使用 Map 的场景:
4. 判断键值相等的问题
因为在 Map 对象中键可以是任意值,所以对键的说明有以下几点:
-
NaN
是与NaN
相等的(虽然NaN !== NaN
),剩下所有其它的值是根据===
运算符的结果判断是否相等; - 在目前的 ECMAScript 规范中,
-0
和+0
被认为是相等的,尽管这在早期的草案中并不是这样。
5. 小结
本节我们深入地学习了 Map 的使用情况,并对比 Object 给出了几个使用场景的参考。学习完本章你需要知道以下几点: