Map

20 阅读3分钟

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 属性的性能一直以来饱受诟病
MapObject
键的类型任意类型String/Symbol
插入顺序保持插入顺序按照属性的一定规则排序: 1、非负整数 2、字符串、负整数、浮点数,根据插入顺序 3、Symbol
迭代器Map 是可迭代对象,支持for-of 和forEachObject 默认不可迭代,使用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 来实现

  • 部署私有属性