每周分享:Map 存在的价值是什么?

前面我们讲过一种条件语句的优化方式,利用映射(map)来替代多个 if…else 和 switch…case

function sayHi (name) {
  return {
    'tom': 'hello',
    'hxh': 'hi',
    'lucy': 'nihao'
  }[name]
}

这里我们用的 map 实际上就是一个简单的对象字面量,而你可能不知道的是,ES6 给我提供了一个真正的 Map 对象。语法为

new Map([iterable])

比如通过二维数组来生成一个 Map 实例

new Map([
  ['name', 'tom'],
  ['age', 20]
])

// Map(2) {"name" => "tom", "age" => 20}

那么有同学说了,既然已经有了 Object,为什么还要弄个新的 Map 对象出来呢?必然是因为这两则之间存在差异,接下来我们就来看看 Map 相对于 Object 有哪些优势。

增删改查

Map 对键值的增删改查并没有像 Object 那么随意,需要通过指定的方法来操作。

let myMap = new Map()

// 设置
myMap.set('name', 'Tom')

// 获取
myMap.get('name') // "Tom"

// 删除
myMap.delete('name')

可以看到,Map 实际上是规范了上述操作,在涉及频繁增删键值对的场景下会有一定的性能优势。此外还有 clear() 这样的方法用来清空所有的健

myMap.clear()

以及 has() 判断是否存在某个键

myMap.has('name')
// false

键类型

众所周知,Object 的键只能是字符串(或 Symbols),而 Map 的键则可以是任何数据类型 ,

let myMap = new Map()

let k1 = () => {}
let k2 = {}
let k3 = new RegExp(/.+/)

myMap.set(k1, '函数')
myMap.set(k2, '对象')
myMap.set(k3, '正则')

myMap.get(k1) // "函数"
myMap.get(k2) // "对象"
myMap.get(k3) // "正则"

注意,使用对象作为键的时候必须是原对象引用

myMap.get({}) // undefined
myMap.get(()=> {}) // undefined

因为 k2 !== {},而 NaN 比较特殊,虽然 NaN === NaN 但是却会被当成同一个键

myMap.set(NaN, '非数字')
myMap.get(NaN) // "非数字"

键顺序

前面我们讲过,遍历一个对象中的键并非和设置时一样,这里面存在一个重新排序的规则,比如

let obj = {
  100: 'Tom',
  10: 'Lucy'
}
Object.keys(obj)
// ["10", "100"]

所以你是没办法保证按照原来的顺序遍历一个对象的,然而 Map 解决了这个问题,保证了设置和遍历的键顺序一致

let myMap = new Map()

myMap.set(100, 'Tom')
myMap.set(10, 'Lucy')

for (var key of myMap.keys()) {
  console.log(key)
}

// 100
// 10

可见,Map 的遍历顺序并没有受到键值的影响。注意,这里的 myMap.keys() 是一个迭代器,不是一个数组

myMap.keys()
// MapIterator {100, 10}

如果你真想拿到键的数组,可以通过 Array.from() 方法做个转换

Array.from(myMap.keys())
// [100, 10]

同样也可以拿到值的数组

Array.from(myMap.values())
// ["Tom", "Lucy"]

数量

要获取一个对象的键值对数量,需要通过获取它的键或值的数组,然后取 length

Object.keys({
  name: 'Tom',
  age: 20
}).length

// 2

而 Map 则直接提供了一个 size 属性

myMap.size
// 2

遍历

Object 有个很大的遗憾是不能直接使用我们推荐的 for…of 进行遍历,而 Map 则完全可以享受这个功能,而且还有很多花样。

直接以[key, value] 遍历整个 Map 对象

let myMap = new Map()

myMap.set('name', 'Tom')
myMap.set('age', 20)

for (var [key, value] of myMap) {
  console.log(key + " = " + value)
}

// name = Tom
// age = 20

遍历键 keys()

for (var key of myMap.keys()) {
  console.log(key)
}
// name
// age

遍历值 values()

for (var value of myMap.values()) {
  console.log(value)
}
// 'Tom'
// 20

可见,在遍历迭代这一块,Map 有很大的优势。

最新评论

发表评论

邮箱地址不会被公开。 必填项已用*标注