Map和Object有非常多相似的地方需要我们去更深入的了解和对比,才能分析出他们分别更适合的应用场景。
什么是Map
Map是一种数据结构(它很特别,是一种抽象的数据结构类型),数据一对对进行存储,其中包含键以及映射到该键的值。并且由于键的唯一性,因此不存在重复的键值对。
Map便是为了快速搜索和查找数据而生的。
例如:{(1, "smile"), (2, "cry"), (42, "happy")}
在Map中,每一对数据的格式都为键值对的形式。
注:Map中的键和值可以是任何数据类型,不仅限于字符串或整数。
什么是Object
JavaScript中的常规对象是一种字典类型的数据结构——这意味着它依然遵循与Map类型相同键值对的存储结构。
Object中的key,或者我们可以称之为属性,同样是独一无二的并且对应着一个单独的value。
另外,JavaScript中的Object拥有内置原型(prototype)。需要注意的是,JavaScript中几乎所有对象都是Object实例,包括Map。
例如:{1: 'smile', 2: 'cry', 42: 'happy'}
从定义上来看,Object和Map的本质都是以键值对的方式存储数据,但实质上他们之间存在很大的区别——
- 键:Object遵循普通的字典规则,键必须是单一类型,并且只能是整数、字符串或是Symbol类型。但在Map中,key可以为任意数据类型(Object, Array等)。(你可以尝试将一个对象设置为一个Object的key,看看最终的数据结构)
- 元素顺序:Map会保留所有元素的顺序,而Object并不会保证属性的顺序。(如有疑问可参考:链接)
- 继承:Map是Object的实例对象,而Object显然不可能是Map的实例对象。
var map = new Map([[1,2],[3,4]]);
console.log(map instanceof Object); //true
var obj = new Object();
console.log(obj instanceof Map); //false
Object和Map的应用场景
Map相对于Object有很多优点,依然存在某些使用Object会更好的场景,毕竟Object是JavaScript中最基础的概念。
- 如果你知道所有的key,它们都为字符串或整数(或是Symbol类型),你需要一个简单的结构去存储这些数据,Object是一个非常好的选择。构建一个Object并通过知道的特定key获取元素的性能要优于Map(字面量 vs 构造函数,直接获取 vs get()方法)。
- 如果需要在对象中保持自己独有的逻辑和属性,只能使用Object。
- JSON直接支持Object,但尚未支持Map。因此,在某些我们必须使用JSON的情况下,应将Object视为首选。
- Map是一个纯哈希结构,而Object不是(它拥有自己的内部逻辑)。使用delete对Object的属性进行删除操作存在很多性能问题。所以,针对于存在大量增删操作的场景,使用Map更合适。
- 不同于Object,Map会保留所有元素的顺序。Map结构是在基于可迭代的基础上构建的,所以如果考虑到元素迭代或顺序,使用Map更好,它能够确保在所有浏览器中的迭代性能。
- Map在存储大量数据的场景下表现更好,尤其是在key为未知状态,并且所有key和所有value分别为相同类型的情况下。
Map和Object性能测试
性能区别
- 当key为有序连续的整数时,Object的性能优于Map;(V8对Object在键为有序连续正整数时做了优化)
- 当key为字符串、非有序连续整数、Symbol时Map的 添加 和 读取 性能优于Object,修改 和 删除 操作性能相差不大;(Object会把键转为String类型,消耗了一部分性能)
- 当key为其他数据类型时,只能选择Map;(Object的键只能为string、symbol类型)
其他区别
- Object可以通过多种方式(字面量、new Object()、Object.create()等)创建,其中字面量的方式方便快捷。Map只能通过构造函数方式创建;
- Map本身具有size属性,Object需要使用 keys()、values()等方法获取;
- Map本身具有可迭代属性,Object不具有;
- Map会保持数据的插入顺序,Object不会;
具体测试代码:
代码语言:javascript复制function createRandomKey() {
return new Date().getTime().toString().substr(6, 7) '-' (Math.random() * 100000000).toString().substr(0, 7);
}
let keys = []
function setKeys() {
for (let i = 0; i < 1000000; i ) {
keys.push(createRandomKey())
}
}
setKeys()
let obj = new Object()
let map = new Map()
function getObjectTimeDiff() {
let t1 = new Date().getTime()
for (let i in keys) {
obj[keys[i]] = i
}
let t2 = new Date().getTime()
for (let j in keys) {
let value = obj[keys[j]]
}
let t3 = new Date().getTime()
for (let k in keys) {
obj[keys[k]] = keys[k]
}
let t4 = new Date().getTime()
for (let l in keys) {
delete obj[keys[l]]
}
let t5 = new Date().getTime()
return `object 增:${t2 - t1},读:${t3 - t2},改:${t4 - t3},删:${t5 - t4}`
}
function getMapTimeDiff() {
let t1 = new Date().getTime()
for (let i in keys) {
map.set(keys[i], i)
}
let t2 = new Date().getTime()
for (let j in keys) {
let value = map.get(keys[j])
}
let t3 = new Date().getTime()
for (let k in keys) {
map.set(keys[k], keys[k])
}
let t4 = new Date().getTime()
for (let l in keys) {
map.delete(keys[l])
}
let t5 = new Date().getTime()
return `map 增:${t2 - t1},读:${t3 - t2},改:${t4 - t3},删:${t5 - t4}`
}
console.log(getObjectTimeDiff())
console.log(getMapTimeDiff())
再修改代码,将keys改为下标的的集合,测试key为连续的整数时
代码语言:javascript复制function setKeys() {
for (let i = 0; i < 1000000; i ) {
keys.push(i)
}
}
参考文章:
【译】Object与Map的异同及使用场景 https://juejin.cn/post/6844903792094232584
Map和Object性能测试https://juejin.cn/post/6992874755070099492
转载本站文章《再谈Object与Map的使用场景分析:性能对比分析》, 请注明出处:https://www.zhoulujun.cn/html/webfront/ECMAScript/js/2023_0418_8940.html