Map
Map 对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值) 都可以作为一个键或一个值。
Object和map的比较
Objects 和 Maps 类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Maps 使用。不过 Maps 和 Objects 有一些重要的区别,在下列情况里使用 Map 会是更好的选择
-
一个 Map 的键可以是任意值,包括函数、对象或任意基本类型。而一个Object 的键必须是一个 String 或是Symbol 。 如下示例代码,我们使用一个方法作为键值再判断输出,我们可以看到object 中如果使用方法作为键值,会将方法体转化为字符串当做键值,而map 则是直接引用了方法作为键值 const obj = {}
const func = ()=>{}
obj[func] = "funcValue"
?
const map = new Map()
map.set(func,"funcValue")
?
console.log(obj)
?
console.log(map)
?
-
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)
}))
?
?
map.forEach((item)=>{
console.log(item)
})
?
深入分析
从上面的例子中我们可以看到,Map 对象其实是像Array 和Object 的合并版,那么我们就根据Web MDN中用的几个例子深入了解一下Map 对象
-
合并操作 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)
console.log(mergeArrMap.size)
?
Object.assign(map,obj)
console.log(map)
console.log(map.size)
上面的例子可以看到为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啊', '赵四' => '我是赵四啊' }
-
性能 根据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()
?
const 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()
不知道是不是使用场景不对,测试了几次都是原始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 。
|