Map 是 ES6 新增的一种集合类型,他是真正的键/值存储机制,在 ES6 之前通过对象存储“键值对”。
每一个 Map 实例都有 size 属性,该属性表示该实例有多少对键值对。
const map1 = new Map([["key", "val"]]);
console.log(map1.size);// 1
使用 new 关键字创建一个 Map 实例,可以接收一个二维数组,作为初始化的数据。
const map1 = new Map();
const map2 = new Map([["key", "val"]]);
console.log(map2.size)//1
const map3 = new Map([[]]);
console.log(map3.has(undefined))//true
console.log(map3.get(undefined))//undefined
创建实例后可以通过 set 方法添加键值对。第一个参数是 键,第二个参数是 值。可以连续调用。
const map1 = new Map();
console.log(map1.size);//0
map1.set("key", "val");
console.log(map1.size);//1
map1.set("key1", "val1").set("key2", "key2");
console.log(map1.size);//3
通过 get 方法获取 Map 实例的键对应的值。如果作为参数的键不在 Map 中则返回 undefined。接收一个参数即键。
const map1 = new Map();
map1.set("key", "val");
console.log(map1.get("key"))//val
console.log(map1.get("key1"))//undefined
通过 has 方法判断该键是否存在 Map 实例中。存在返回 true,不存在返回 false。
const map1 = new Map([["key", "val"]]);
console.log(map1.has("key"))//true
console.log(map1.has("key1"))//false
通过 delete 方法删除键值对。传入要删除的键。要删除的键在实例中则删除并返回 true,否则返回 false。
const map1 = new Map([["key", "val"]]);
console.log(map1.delete("key"))//true
console.log(map1.size);
console.log(map1.delete("key1"))//false
通过 clear 方法清空 Map 实例的键值对。
const map1 = new Map([["key", "val"]]);
console.log(map1.size);//1
map1.clear();
console.log(map1.size);//0
Map 可以使用任意 JS 类型作为键,但是 Object 只能以数值、字符串、符号作为键。这意味着 Map 可以使用对象作为键。并且 Map 使用 SameValueZero(类似于严格对象相等)来确定键的匹配性。所以当对象作为键时,更改对象的属性并不会丢失映射关系。
const obj = {}
const map1 = new Map([[obj, "val"]]);
console.log(map1.get(obj));//val
obj.name = "123";
console.log(map1.get(obj));//val
Map 能够按照键值对的插入顺序迭代输出。并且 Map 实例提供了一个迭代器以便能够通过特特定方法按顺序迭代。
调用本方法会返回一个迭代器,并且是默认迭代器。所以可以使用扩展操作符,使用 for of 迭代 Map 实例时可以省略显示调用 entries();
const map1 = new Map([[1, 1], [2, 2]]);
for (let item of map1.entries()) {
console.log(item);
}
console.log("-----------");
for (let item of map1) {
console.log(item);
}
console.log("-----------");
console.log([...map1]);
调用本方法会返回一个按照插入顺序的键的迭代器。
const map1 = new Map([[1, 1], [2, 2]]);
for (let item of map1.keys()) {
console.log(item);
}
调用本方法会返回一个按照插入顺序的值的迭代器。
const map1 = new Map([[1, 1], [2, 2]]);
for (let item of map1.values()) {
console.log(item);
}
const map1 = new Map([[1, 1], [2, 2]]);
map1.forEach((val, key) => {
console.log(key + ":" + val);
})
需要注意的是如果在迭代过程中修改给了每次迭代的结果,并不会影响映射关系,如下例
const map1 = new Map([[1, 1], [2, 2]]);
for (let item of map1.keys()) {
item = 3;
console.log(item);
}
console.log(map1.get(1));
由输出结果可知在遍历的过程中使 item 的值为 3,因此输出两次 3,最后通过原键仍然拿到了值。
大部分情况下使用 Object 和 Map 区别并不大,下面只是给出一些小小的建议。
weakMap 也是 ES6 新增的一种集合类型,它有如下特点:
JS 有一种垃圾回收策略叫做引用计数,通俗理解就是声明一个对象后,JS 就会使用一个变量用来保存该对象被使用的次数,如果被使用次数为 0 ,那么 JS 就会销毁该对象,释放内存。
给个例子
const obj1 = { name: 1 }, obj2 = { name: 2 };
const map = new WeakMap([[obj1, 1], [obj2, 2]]);
这里弱引用的意思是当我实例化了一个 weakMap 集合 map,并且使用 obj1 和 obj2 作为键值对的键存入 map 中,那么此时 map 使用 obj1 和 obj2,但是由于 map 是 weakMap 实例,所以 obj1 和 obj2的被使用次数并不会加 1,那么一旦 obj1 和 obj2 又没有在别的地方被使用,JS 就会回收 obj1 和 obj2,map 就失去了键,自然获取不到值。
因此 weakMap 实例没有提供迭代器,因为也许你刚迭代完,该键就被回收了,内存都被释放了,那么此时的迭代就没有了意义。也正因此 weakMap 舍弃了 clear API。并且即使能够访问 weakMap 实例也无法看到其存储的键值对。
但是需要注意的是 weakMap 的值并不是弱引用,只要键存在,那么它对应的值一定不会被 JS 回收。
还需要注意 weakMap 的好兄弟 Map 就不是这样,Map 实例对于键的应用是强引用,只要 Map 实例没被回收,那么其存储键值对就会一直存在,不会被 JS 私自回收。
我是孤城浪人,一名正在前端路上摸爬滚打的菜鸟,欢迎你的关注。