ES6的Map和Set的使用,以及weakMap的一点理解

·  阅读 621

一、Map

Map可以用来存储键值对,在一定程度上扩展了Object的内容。

1. Map的基本API

创建新的Map实例

// 创建一个空映射
let map = new Map()  
// 传入一个包含键值对组的可迭代对象,
// 传入的可迭代对象会按顺序插入到新的map实例里
let arr = [
    ['key1', 'val1'],
    ['key2', 'val2'],
    ['key3', 'val3']
]
let map1 = new Map(arr) 
// 同样也可以是自定义的迭代对象
复制代码
  • set(key, value) 添加新的键值对,返回一个一个映射实例(所以可以链式操作)
  • get(key) 根据键名返回对应的键值
  • size属性,返回当前存储的键值对数量
  • has(key) ,返回是否有当前传入的键名是否存在,返回boolean类型
  • delete(key), 删除指定的键值对
  • clear() 清空映射,删除全部的键值对
let m = new Map()

// 添加新的键值对
m.set('name', 'zhangsan')
 .set('age', 20)

// 获取对应的键值
console.log(m.get(name));

// 确定键名是否在实例上
console.log(m.has('name'));

// 确定存储的键值对的数量

console.log(m.size);

// 删除指定的键值对
m.delete('name')

// 清空映射,删除全部的键值对
m.clear()
复制代码

Map的一些迭代方法

在插入内容时,map实例会维护插入顺序,遍历出来的顺序是插入顺序,而object的遍历顺序可能不是插入顺序。所以下面三个方法作用是合object中一样的,只是map调用会按照键值对的插入顺序迭代

  • entries 与迭代map自身相同,map本身也是可迭代的
  • keys
  • values

不同于对象的方法

  • forEach 不同于数组的forEach方法,map的forEach方法回调函数里面的两个参数一个是value,一个是key,函数的第二个参数同数组方法,是回调函数内部this的值
let m = new Map([
  ['key1', 'value1'],
  ['key2', 'value2'],
  ['key3', 'value3']
])

m.forEach((value, key) => {
  console.log(value, key);
})
// value1 key1
// value2 key2
// value3 key3
复制代码

Map解决了Object的什么问题

Object只能使用数字(number),字符串(string),符号(Symbol),作为键名,Map可以使用任何类型作为建名。两者之间相同的是对键值都没有限制,object在用其他类型作为键值时会toString进行转换。所以当需要存储更多类型时,选用Map

选择Object和Map

  1. 内存方面

同样大小的内存,map比Object存的多,所以Map胜, 2. 插入时 插入时两种方式差不多,插入大量数据时,还是选择map 3. 查找速度 差异极小,使用连续整数作为属性时,调用浏览器可以进行优化,可能速度会高点 4. 删除性能 删除时,map更优

Set

基本api

创建新的Set实例

//  创建一个空集合
let s = new Set()

// 传入可迭代对象,包含插入到集合中的新元素
let s = new Set([1, 2, 3])
复制代码
  • add(item) 添加新元素,类似map的set方法,返回一个新的实例,所以可以链式调用
  • has(item) 查询是否有指定元素
  • size 属性, 查看集合的元素数量
  • delete(item) 删除指定的元素,返回布尔值,表示是否存在要删除的元素
  • clear() 清除集合,删除所有元素

迭代

set在插入时也会维持顺序,按插入顺序迭代

  • keys()
  • values()
  • 自身可迭代

三种操作结果相同,都是迭代出每个元素

for(let i of s.values()) {
  console.log(i);
}
for(let i of s.keys()) {
  console.log(i);
}
for(let i of s) {
  console.log(i);
}

复制代码
  • entries,产生包含两个集合中重复元素的数组
let s = new Set([1, 2, 3])

for(let i of s.entries()) {
  console.log(i);
}

// [1, 1]
// [2, 2]
// [3, 3]
复制代码

关于weakMap的理解,

weakMap根据名字就可以看出来,它是一种弱的map映射关系,即WeakMaps保持了对键名所引用的对象的弱引用,弱引用就是在任何时候都有可能被垃圾回收机制回收掉的引用。直接声明的对象如

let o = {} // 这样创建一个对象是强引用,强引用是不会被垃圾回收的
// 只有在设置了null时,才会被回收
o = null // 可以被垃圾回收了
复制代码

当声明了一个对象,而Map又引用这个对象作为键名时

let o = {name: 'zhangsan'}

let m = new Map()

// map强引用了o作为键值
m.set(o, 1) 

// 当设置o= null 时,只是清掉了o对对象的引用
// 但是m仍然引用着对象,所以并不能清除

// 想要被清除掉,首先得先delete(key) 然后在使用key= null,才能清除
复制代码

由此,weakMap的作用就显现出来了,由于它的键值对是弱引用,若依,当对象key=null时,因为他本来键值就是弱引用,所以等到下一轮垃圾回收执行时,该引用对象就会被回收掉. 所以weakmap的作用就是保留了对键名所引用的对象的弱引用,即,如果键名所引用的对象不被其他变量引用,那么垃圾回收就会释放掉对象所占内存,因为弱引用随时都有可能被垃圾回收清除,垃圾回收机制执行实际不可预测,所以WeakMap也就不能够遍历,简单的例子来说明

  • 当weakMap的键值没有被引用时
let wm = new WeakMap()
// 空对象没有被任何值引用,只是作为了键值
// 所以,直接会被垃圾回收掉,键值对被破坏,值本身也会被垃圾回收
wm.set({}, 1)
console.log( wm.get({})) // undefined
复制代码
  • 当值有被引用时
let wm = new WeakMap()

// 创建一个对象引用着key
let obj = {
  key: {}
}
// 在实例里用key作为键值
wm.set(obj.key, 1)

// 可以访问该键值对
console.log(wm.get(obj.key));  // 1

// 当清除掉obj对key的引用时,在wm中也就没有了对应的键值对映射
obj.key = null
console.log(wm.get(obj.key)); // undefined
复制代码
  • WeakMap的四个方法,
  • get()
  • set()
  • has()
  • delete()

Weak中的建只能是对象

WeakMap的应用场景

1. 数据缓存

当我们需要在不修改原对象的情况下存储某些属性或者根据对象存储一些计算的值,而又不想关系这些值是否被回收掉时,可以使用WeakMap

// 比如说保存对象属性的长度的缓存
const cache = new WeakMap()

function keyLength(obj) {
  // 如果当前缓存中有这个数据就直接返回
  if(cache.has(obj)) {
    return cache.get(obj)
  } else {
    // 如果没有,就把当前对象的键的长度存在缓存中
    const count = Object.keys(obj).length
    cache.set(obj, count)
    // 返回这次计算的长度
    return count
  }
}
复制代码

2. dom中的数据

有时候可能会对dom节点关联一些数据,比如是否禁用什么的,可以用映射使他们关联起来,但是如果有一些操作可能需要删除掉这个dom节点,这个时候如果用普通的map的话,dom节点虽然删除了,但是他仍然在map中被引用着,所以耗费内存,这时,使用weakMap就能实现,如下。

let wm = new WeakMap()

const btn = document.querySelector('#btn')

// 给btn节点添加一些关联数据等
m.set(btn, {disabled:true})
复制代码

参考文献

分类:
前端
标签:
分类:
前端
标签:
收藏成功!
已添加到「」, 点击更改