JavaScript ES(6-11)全版本语法 (九):Map

1,298 阅读4分钟

前提概要

上一篇编写的是ES6中的Set,链接:juejin.cn/post/701244… , 这次写的是ES6中Map的基本语法和WeakMap,可能不是很全。如有不对的或者不准确的地方,欢迎大家提出😄,我也积极修改。下边开始正文:

src=http___p2.itc.cn_images01_20210429_4da028c2946246d28603fe878f70e31b.jpeg&refer=http___p2.itc.jpg

基本语法

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 之间的有什么区别呢?

  1. 键的类型

    一个Object的键只能是字符串或者 Symbols,但一个 Map 的键可以是任意值,包括函数、对象、基本类型。

  2. 键的顺序

    Map 中的键值是有序的,而添加到对象中的键则不是。因此,当对它进行遍历时,Map 对象是按插入的顺序返回键值。

  3. 键值对的统计

    你可以通过 size 属性直接获取一个 Map 的键值对个数,而 Object 的键值对个数只能手动计算。

  4. 键值对的遍历

    Map 可直接进行迭代,而 Object 的迭代需要先获取它的键数组,然后再进行迭代。

  5. 性能

    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的区别有以下几点:

  1. 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
  1. 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内存放的键值对会自动释放掉,不会占用内存空间,避免了内存泄漏。