前提概要
上一篇编写的是ES6中的Set,链接:juejin.cn/post/701244… , 这次写的是ES6中Map的基本语法和WeakMap,可能不是很全。如有不对的或者不准确的地方,欢迎大家提出😄,我也积极修改。下边开始正文:
基本语法
ES6提供了Map
数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的Hash
结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
let m = new Map([iterable])
Iterable 可以是一个数组或者其他 iterable 对象,其元素为键值对。每个键值对都会添加到新的 Map。null 会被当做 undefined。
添加数据
// 添加键
let m = new Map()
let obj = {
name: 'xs'
}
m.set(obj, 'es')
console.log(m)
运行结果:
Map(1) {{…} => 'es'}
[[Entries]]
0: {Object => "es"}
key: {name: 'xs'}
value: "es"
size: 1
[[Prototype]]: Map
删除数据
let m = new Map()
m.set('name','xs').set('age',18)
// 或者let m = new Map([['name','xs'],['age',18]])
// 删除指定的数据
m.delete('name')
console.log(m) // Map(1) {'age' => 18}
// 删除所有数据
map.clear() // Map(0) {size: 0}
统计数据
let m = new Map([['name','xs'],['age',18]])
// 统计所有 key-value 的总数
console.log(m.size) //2
// 判断是否有 key-value
console.log(m.has('name')) // true
查询数据
get()
方法返回某个 Map 对象中的一个指定元素
let m = new Map()
let obj = {
name: 'xs'
}
m.set(obj,'es')
console.log(m.get(obj)) // es
遍历
- forEach() 方法将会以插入顺序对 Map 对象中的每一个键值对执行一次参数中提供的回调函数
- keys() 返回一个新的 Iterator 对象。它包含按照顺序插入 Map 对象中每个元素的 key 值
- values() 方法返回一个新的 Iterator 对象。它包含按顺序插入Map对象中每个元素的 value 值
- entries() 方法返回一个新的包含 [key, value] 对的 Iterator 对象,返回的迭代器的迭代顺序与 Map 对象的插入顺序相同
- for...of 可以直接遍历每个成员
let map = new Map([
['name', 'xs'],
['age', 18]
])
map.forEach((value, key) => console.log(value, key)) // xs name 18 'age'
for (let key of map.keys()) {
console.log(key) // name age
}
for (let value of map.values()) {
console.log(value) // xs 18
}
for (let [key, value] of map.entries()) {
console.log(key, value) // name xs age 18
}
for (let [key, value] of map) {
console.log(key, value) // name xs age 18
}
其实 Object 也是按键值对存储和读取的,那么 Object 和 Map 之间的有什么区别呢?
-
键的类型
一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。
-
键的顺序
Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。
-
键值对的统计
你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。
-
键值对的遍历
Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。
-
性能
Map 在涉及频繁增删键值对的场景下会有些性能优势。
WeakMap
WeakMap结构与Map结构类似,也是用于生成键值对的集合。但 WeakMap 的键名只支持引用数据类型,例如:数组,对象,function。
let wm = new WeakMap()
// WeakMap 可以使用 set 方法添加成员
wm.set([1], 2)
console.log(wm) // WeakMap {Array(1) => 2}
wm.set({ name: 'xs' }, 18)
console.log(wm) // WeakMap {Array(1) => 2, {…} => 18}
// WeakMap 也可以接受一个数组,作为构造函数的参数
const m1 = [1, 2, 3]
const m2 = [4, 5, 6]
const wm2 = new WeakMap([
[m1, 'foo'],
[m2, 'bar']
])
wm2.get(m2) // 'bar'
WeakMap与Map的区别有以下几点:
- WeakMap只接受对象(引用数据类型)作为键名(null除外),不接受其他类型的值作为键名。
const map = new WeakMap()
map.set(1, 2) // Uncaught TypeError: 1 is not an object!
map.set(Symbol(), 2) // Uncaught TypeError: Invalid value used as weak map key
map.set(null, 2) // Uncaught TypeError: Invalid value used as weak map key
- WeakMap的键名所指向的对象,不计入垃圾回收机制。
let wm = new WeakMap()
wm.set({ name: 'xs' }, 18)
console.log(wm) // WeakMap {{…} => 18}
// wm.clear() // Uncaught TypeError: wm.clear is not a function
console.log(wm.size) // undefined
由上述代码可知,WeakMap 不支持 clear() 方法,而且没有 size 属性,所以也不支持遍历。
因此,WeakMap 和 WeakSet 一样,也是一种弱引用。意思就是说如果这种引用类型的值被垃圾回收机制回收了,那么它里面实例所对应的键值对也会自动消失。
注意: WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
const wm = new WeakMap();
let key = {};
let obj = {foo: 1};
wm.set(key, obj);
obj = null;
wm.get(key)
// Object {foo: 1}
应用场景
假如当前页面内有个h1标签,我们要把这个标签进行存储,在不需要时被自动被移除,就可以使用 WeakMap
let wm = new WeakMap()
let elem = document.getElementsByTagName('h1')
wm.set(elem, 'info')
console.log(wm.get(elem)) // 'info'
在上述代码中,若其他位置对该对象的引用一旦消除,相当于当前垃圾回收机制被调用的次数为0,该对象占用的内存就会被垃圾回收机制释放。WeakMap 保存的这个键值对,会自动消失,因此wm内存放的键值对会自动释放掉,不会占用内存空间,避免了内存泄漏。