js中object,map和weakMap的简析和区别

js中object,map和weakMap的简析和区别_第1张图片

Map

Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。

Object和map的比较

ObjectsMaps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps 使用。不过 MapsObjects 有一些重要的区别,在下列情况里使用 Map 会是更好的选择

  1. 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。而一个Object 的键必须是一个 String 或是Symbol

    如下示例代码,我们使用一个方法作为键值再判断输出,我们可以看到object中如果使用方法作为键值,会将方法体转化为字符串当做键值,而map则是直接引用了方法作为键值

    const obj = {
           }
    const func = ()=>{
           }
    obj[func] = "funcValue"const map = new Map()
    map.set(func,"funcValue")
    ​
    console.log(obj)// { '()=>{}': 'funcValue' }
    ​
    console.log(map)// Map(1) { [Function: func] => 'funcValue' }
  2. Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值,Map 的键值对个数可以轻易地通过size属性获取,Map 是可迭代的(iterable)的。而 Object 的键是无序的,Object 的键值对个数只能手动计算,迭代一个Object需要以某种方式获取它的键然后才能迭代。

    这就意味着map拥有着类似数组的属性,而且Map对象可以直接使用数组原型的一些方法例如 for…of… ,foreach等等`

    const obj = {
           }
    const func = ()=>{
           }
    obj[func] = "funcValue"const map = new Map()
    map.set(func,"funcValue")
    ​
    console.log(obj.forEach((item)=>{
           
      console.log(item) // obj.forEach is not a function
    }))
    ​
    ​
    map.forEach((item)=>{
           
      console.log(item) // funcValue
    })

深入分析

从上面的例子中我们可以看到,Map对象其实是像ArrayObject的合并版,那么我们就根据Web MDN中用的几个例子深入了解一下Map对象

  1. 合并操作

    Map对象可以与数组或者对象进行合并,如下例

    const map = new Map()
    map.set('a',"这里是a")
    const obj = {
           b:'这里是b'}
    const arr = ['c','这里是c']
    ​
    ​
    const mergeArrMap = new Map([...map,arr])
    console.log(mergeArrMap) // Map(2) { 'a' => '这里是a', 'c' => '这里是c' }
    console.log(mergeArrMap.size) // 2
    ​
    Object.assign(map,obj)
    console.log(map) // Map(1) { 'a' => '这里是a', b: '这里是b' }
    console.log(map.size) // 1
    

    上面的例子可以看到为Map设置对象属性也是可以的,但是可能引起大量的混乱。在调用api的时候返回与我们预期不符的结果。

    所以我们最好还是使用map的api进行操作,例如:

    const map = new Map()
    map.set(key,value)
    map.delete(key)
    

js中的map对于学过php同学来说是非常类似php中的对象数组的,个人理解Map对象就是一个二维数组,内层数组只取前两个值,第一个值是外层数组的键,第二个值是外层数组的值,因此 Map对象 具有类似数组和对象功能和api

const arr = [
  ['a','我是a啊'],
  ['b','我是b啊'],
  ['c','我是c啊','笑死,爷还有一个值'],
  ['赵四','我是赵四啊']
]
const map = new Map(arr)
console.log(map) // Map(3) { 'a' => '我是a啊', 'b' => '我是b啊', '赵四' => '我是赵四啊' }
  1. 性能

    根据MDN所说在Map对象频繁增删键值对的场景下表现更好。我们写一个简单的例子测试一下

    const obj = {
           }
    ​
    console.time()
    for(let i =0 ;i<=1000000;i++){
           
      obj[i] = '我是对象的值'+i
    }
    for(let i =1000000 ;i>0;i--){
           
      delete obj[i]
    }
    console.timeEnd() //322.576msconst map = new Map()
    console.time()
    for(let i =0 ;i<=1000000;i++){
           
      map.set(i,'我是map的值'+i)  
    }
    for(let i =1000000 ;i>0;i--){
           
      map.delete(i)  
    }
    console.timeEnd() //390.84ms
    

    不知道是不是使用场景不对,测试了几次都是原始object的增删速度比较快,如果有了解合适场景的小伙伴欢迎补充

WeakMap

WeakMap 对象是一组键/值对的集合,其中的键是弱引用的。其键必须是对象,而值可以是任意的。

Object和map的比较

在 JavaScript 里,map API 可以通过使其四个 API 方法共用两个数组(一个存放键,一个存放值)来实现。给这种 map 设置值时会同时将键和值添加到这两个数组的末尾。从而使得键和值的索引在两个数组中相对应。当从该 map 取值的时候,需要遍历所有的键,然后使用索引从存储值的数组中检索出相应的值。

但这样的实现会有两个很大的缺点,首先赋值和搜索操作都是 O(n) 的时间复杂度( n 是键值对的个数),因为这两个操作都需要遍历全部整个数组来进行匹配。另外一个缺点是可能会导致内存泄漏,因为数组会一直引用着每个键和值。这种引用使得垃圾回收算法不能回收处理他们,即使没有其他任何引用存在了。

相比之下,原生的 WeakMap 持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行。原生 WeakMap 的结构是特殊且有效的,其用于映射的 key 只有在其没有被回收时才是有效的。

1.一个 Map的键可以是任意值,包括函数、对象或任意基本类型。而weakMap其键必须是对象,而值可以是任意的。一下为weakMap的使用方式

const map = new WeakMap()
let obj = {
     '赵四':'我是赵四'}
​
map.set(obj,'我还是赵四')
​
​
console.log(map.get(obj)) // 我还是赵四

在vue3.x版本源码中就用到了weakMap作为响应方法的一个存储方式,这样做的一个好处是可以防止内存泄露,如MDN所说如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap

你可能感兴趣的:(js,javascript,js,vue,vue.js)