Map
ES6 提供了Map 数据结构,键的范围不限于字符串,各种类型的值(包括对象)都可以当作键。
Map 构造函数接受数组作为参数:
const items = [['name', '张三'], ['title', 'Author']]
const map = new Map()
items.forEach((key, value) => map.set(key, value))
只用对同一个对象的引用,Map 结构才将其视为同一个键。
Map 的键实际上是和内存地址绑定的,只要内存地址不一样,就视为两个键。
如果Map 的键是一个简单类型的值(数字、字符串、布尔值),则只要两个值严格相等,Map 就将其视为一个键,包括0 和-0。虽然NaN 不严格等于自身,但Map 将其视为同一个键。
实例的属性和方法
Map 实例会维护键值对的插入顺序,因此可以根据插入顺序执行迭代操作。
// 属性
size
// 操作方法
set(key, value)
get(key)
has(key)
delete(key)
clear()
// 迭代器
entries()
keys()
values()
// 使用回调方法
forEach()
// forEach 方法可以接受第二个参数,用于绑定this
const reporter = {
report: function(key, value) {
console.log('Key: $s, Value: $s, key, value')
}
}
map.forEach(function(value, key, map) {
this.report(key, value)
}, reporter)
Object vs Map
对于在乎内存和性能的开发者来说,对象和映射之间确实存在显著的差别。
-
内存占用
- 给定固定大小的内存,Map 大约可以Object 多存储50% 的键/值对
-
插入性能
- 向Object 和Map 中插入新键/值对的消耗大致相当,不过插入Map 在所有浏览器中一般会稍微快点。对这两个类型来说,插入速度不会随着键/值对数量而线性增加。如果代码涉及大量插入操作,那么显然Map 的性能更佳
-
查找速度
- 从大型Object 和Map 中查找键/值对的性能差异极小,但如果只包含少量键/值对,则Object 有时候速度更快
-
删除性能
- 使用delete 删除Object 属性的性能一直以来饱受诟病
Map | Object | |
---|---|---|
键的类型 | 任意类型 | String/Symbol |
插入顺序 | 保持插入顺序 | 按照属性的一定规则排序: 1、非负整数 2、字符串、负整数、浮点数,根据插入顺序 3、Symbol |
迭代器 | Map 是可迭代对象,支持for-of 和forEach | Object 默认不可迭代,使用for-in 或者方法循环(Object.keys(0)/values/entries) |
原型属性 | 默认不会覆盖原型的值 | 可覆盖原型的值 |
操作方法 | 使用new 创建 mapObj.set(‘a’, 1) mapObj.get(‘a’) mapObj.delete(‘a’) 长度 mapObj.size() | Object 使用点语法或者[] 删除 delete obj.a 长度 Object.keys(testObj).length |
WeakMap
weak 描述的是JS 垃圾回收程序对待“弱映射”中键的方式:
- WeakMap 只接受对象作为键名(null 除外),不接受其他类型的值作为键名
- 原始值可以先包装成对象再用作键
- WeakMap 的键名所指向的对象不计入垃圾回收机制
- 方法:get、set、has、delete
应用场景:
-
典型场景就是以DOM 节点作为键名的场景
// Map const m = new Map() const loginButton = document.querySelector('#login') // 给这个节点关联一些元数据 m.set(loginButton, { disabled: true }) // 假设在上述代码执行后,页面被JS 改变了,原来的登录按钮从DOM 树中被删掉了。但由于映射中还保存着按钮的引用,所以对应的DOM 节点仍然会逗留在内存中,除非明确将其从映射中删除或者等到映射本身被销毁。 // WeakMap const m = new WeakMap() const loginButton = document.querySelector('#login') m.set(loginButton, { disabled: true }) // 当节点从DOM 树中被删除后,垃圾回收程序就可以立即释放其内存(假设没有其他地方引用这个对象)
-
注册监听事件的listener 对象很适合用WeakMap 来实现
-
部署私有属性