使用Object( ) 还是 Map( ) ?

370 阅读3分钟

什么是Map

Map是ES6新引入的内置对象,引用MDN对它的介绍

Map 对象是键值对的集合。Map 中的一个键只能出现一次;它在 Map 的集合中是独一无二的。Map 对象按键值对迭代——一个 for...of 循环在每次迭代后会返回一个形式为 [key,value] 的数组。迭代按插入顺序进行,即键值对按 set() 方法首次插入到集合中的顺序(也就是说,当调用 set() 时,map 中没有具有相同值的键)进行迭代。

规范要求 map 实现“平均访问时间与集合中的元素数量呈次线性关系”。因此,它可以在内部表示为哈希表(使用 O(1) 查找)、搜索树(使用 O(log(N)) 查找)或任何其他数据结构,只要复杂度小于 O(N)。

Map 与 Obejct 的区别

MapObject
addmap.set(key, value)obj[key] = value
getmap.get(key)obj[key]
deletemap.delete(key)delete obj[key]
sizemap.sizeObject.keys(obj).length
valuesmap.values()Object.values(obj)
keys任意JavaScript数据类型string symbols
default keys暂无JavaScript对象的公共属性
traversalfor...of map.forEach(...)for...in Object.keys(obj).forEach(...)

不用在意覆盖默认值

new Map

image.png

new Object

image.png

JS的设计理念中,对象是一切复杂数据类型的基础,所以创建一个空对象时,会存在大量的基础属性在其原型上,所以在使用时,键值对可能会存在冲突,而Map则不会出现这个情况。

可以使用任意数据类型

Object中只有stringsymbol类型的数据,而Map则可以使用任何JS的数据类型,可以帮助我们更好好的处理各种复杂情况。

Map

let obj = { 'a': 'a' };
let func = () => 'hey';

let map = new Map([[123, true], [true, 123], [obj, 'object'], [func, 'function']])

map.keys() // 123, true, Object, () => 'hey'
map.get(obj) // 'object'
map.get(func) // 'function'
map.get({ 'a': 'a' }) // undefined 

Object

let obj1 = { 'a': 'a' };
let func = () => 'hey';

let obj = { 123: true, true: 123, obj1: 'object', func: 'function' };
Object.keys(obj) 
// ['123', 'true', 'obj1', 'func']

obj[func] //undefined
obj['func'] // 'function'

轻松的遍历它们

Map

使用for...offorEach

const map = new Map();
map.set(0, 'zero').set(1, 'one');

for (const [key, value] of map) {
  console.log(`key: ${key}, value: ${value}`);
}
// key: 0, value: zero
// key: 1, value: one

for (const key of map.keys()) {
  console.log(key);
}
// 0
// 1

map.forEach((value, key) => console.log(`key: ${key}, value: ${value}`));
// key: 0, value: zero
// key: 1, value: one

Object

使用for...inObject.entries().forEach

let obj = { 0: 'zero', 1: 'one' }

for(let key in obj){
    console.log(`key: ${key}, value: ${obj[key]}`)
}
// key: 0, value: zero
// key: 1, value: one

Object.entries(obj).forEach((item) => console.log(`key: ${item[0]}, value: ${item[1]}`))
// key: 0, value: zero
// key: 1, value: one

在测试中发现两者的遍历效率,在小数据量下,两者的遍历速度并没有明显差别,但在大数据量下Map的遍历效率会高一些,应该是浏览器引擎进行了特殊处理。

更方便的合并数据

Map

let map = new Map([ [1, 'one'], [2, 'two'] ]);
let arr = [3, 'three']

let combinedMap = new Map(...map, arr); 
// { 1 => 'one', 2 => 'two', 3 => 'three' }

let combinedArr = [...map, arr];
// [ [1, 'one'], [2, 'two'], [3, 'three'] ]

Object

let map = new Map([ [1, 'one'], [2, 'two'] ]);
Array.from(map) //[ [1, 'one'], [2, 'two'] ] 

const newArr = [...map];

MapArray是完全兼容的,而且处理起来更加的直观。

获取它们的Size

let map = new Map([1, 'one'], [true, 'true']);
map.size // 2
let obj = { 1: 'one', true: 'true' };
Object.keys(obj).length // 2

Map的缺点

Map没有对JSON的序列化或解析的原生支持

我们可以通过给JSON.stringify(obj, replacer)replacer参数和传递给JSON.parse(string, reviver)reviver参数来实现。

function replacer(key, value) {
  if(value instanceof Map) {
    return {
      dataType: 'Map',
      value: Array.from(value.entries()),
    };
  } else {
    return value;
  }
}
function reviver(key, value) {
  if(typeof value === 'object' && value !== null) {
    if (value.dataType === 'Map') {
      return new Map(value.value);
    }
  }
  return value;
}

使用方法

const originalValue = new Map([['a', 1]]);
const str = JSON.stringify(originalValue, replacer);
const newValue = JSON.parse(str, reviver);
console.log(originalValue, newValue);

对于Object我们可以使用JSON.stringify()JSON.parse()分别对一个对象进行原生序列化和解析。

尾言

ES6+推广至今,各种新的特性Map Weakmap Set WeakSet Proxy等,早已深入我们的日常开发。但我们还是更加深入了解的这些新特性,通过与旧内容的对比,才能更好的利用它们,提高开发效率。