ES6之Map对象

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

一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个  for...of 循环在每次迭代后会返回一个形式为[key,value]的数组。

  • 键的比较是基于 sameValueZero 算法:
  • NaN 是与 NaN 相等的(虽然 NaN !== NaN),剩下所有其它的值是根据 === 运算符的结果判断是否相等。
  • 在目前的ECMAScript规范中,-0+0被认为是相等的,尽管这在早期的草案中并不是这样。有关详细信息,请参阅浏览器兼容性 表中的“Value equality for -0 and 0”。

Map与普通对象的区别

  Map Object
意外的键

Map 默认情况不包含任何键。只包含显式插入的键。

const func = () => null;
const object = {};
const array = [];
const bool = false;
const map = new Map();

map.set(func, 'value1');
map.set(object, 'value2');
map.set(array, 'value3');
map.set(bool, 'value4');
map.set(NaN, 'value5')

一个 Object 有一个原型, 原型链上的键名有可能和你自己在对象上的设置的键名产生冲突。

注意: 虽然 ES5 开始可以用 Object.create(null) 来创建一个没有原型的对象,但是这种用法不太常见。

const symbol = Symbol();
const string2 = 'string2';

const regularObject = {
  'string1': 'value1',
  [string2]: 'value2',
  [symbol]: 'value3'
};
键的类型 一个 Map的键可以是任意值,包括函数、对象或任意基本类型。 一个Object 的键必须是一个 String 或是Symbol
键的顺序

Map 中的 key 是有序的。因此,当迭代的时候,一个 Map 对象以插入的顺序返回键值。

一个 Object 的键是无序的

注意:自ECMAScript 2015规范以来,对象确实保留了字符串和Symbol键的创建顺序; 因此,在只有字符串键的对象上进行迭代将按插入顺序产生键。

Size

 Map 的键值对个数可以轻易地通过size 属性获取

const map = new Map(); 
map.size属性直接获取长度

Object 的键值对个数只能手动计算

先转换为数组,然后获取数组长度,
如:
Object.keys({}).length

迭代

Map  iterable 的,所以可以直接被迭代。

for (let [key, value] of map) {
  console.log(key);
  console.log(value);
};
map.forEach((key, value) => {
  console.log(key);
  console.log(value);
});

迭代一个Object需要以某种方式获取它的键然后才能迭代。

必须将它们转换为数组,如使用Object.keys()Object.values()Object.entries(),或者使用for ... in循环,因为常规对象不能直接遍历,另外for ... in循环还有一些限制:它仅仅遍历可枚举属性、非Symbol属性,并且遍历的顺序是任意的

性能

在频繁增删键值对的场景下表现更好。

在频繁添加和删除键值对的场景下未作出优化。

let myMap = new Map();
myMap.set(NaN, "not a number");

myMap.get(NaN); // "not a number"

let otherNaN = Number("foo");
myMap.get(otherNaN); // "not a number"   虽然NaN!==NaN

遍历:

let myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (let [key, value] of myMap) {
  console.log(key + " = " + value);
}
// 将会显示两个log。一个是"0 = zero"另一个是"1 = one"

for (let key of myMap.keys()) {
  console.log(key);
}
// 将会显示两个log。 一个是 "0" 另一个是 "1"

for (let value of myMap.values()) {
  console.log(value);
}
// 将会显示两个log。 一个是 "zero" 另一个是 "one"

for (let [key, value] of myMap.entries()) {
  console.log(key + " = " + value);
}
// 将会显示两个log。 一个是 "0 = zero" 另一个是 "1 = one"

MapSet有何不同

Map的行为和Set非常相似,并且它们都包含一些相同的方法,包括:has、get、set、delete。它们两者都是键控集合,就是说你可以使用像forEach的方法来遍历元素,顺序是按照插入键值排列的。

最大的不同是Map通过键值(key/value)成对出现,就像你可以把一个数组转换为Set,你也可以把二维数组转换为Map

const set = new Set([1, 2, 3, 4]);
const map = new Map([['one', 1], ['two', 2], ['three', 3], ['four', 4]]);

类型转换:

 要将Map切换回数组,你可以使用ES6的解构语法:

const map = new Map([['one', 1], ['two', 2]]);
const arr = [...map];

 map对象和常规对象互换:

const mapToObj = map => {
  const obj = {};
  map.forEach((key, value) => { obj[key] = value });
  return obj;
};
const objToMap = obj => {
  const map = new Map();
  Object.keys(obj).forEach(key => { map.set(key, obj[key]) });
  return map;
};

ES2019的首次展示中,我们看见了Object引入了2个新方法:Object.entries()Object.fromEntries(),这可以使上述方法简化许多:

const obj2 = Object.fromEntries(map);
const map2 = new Map(Object.entries(obj));

 

其他:

请注意!为Map设置对象属性也是可以的,但是可能引起大量的混乱。

所以,你还是可以这样做...

let wrongMap = new Map()
wrongMap['bla'] = 'blaa'
wrongMap['bla2'] = 'blaaa2'

console.log(wrongMap)  // Map { bla: 'blaa', bla2: 'blaaa2' }

...但是,这样做的话,它的行为会不符合预期:

wrongMap.has('bla')    // false
wrongMap.delete('bla') // false
console.log(wrongMap)  // Map { bla: 'blaa', bla2: 'blaaa2' }

无论如何,和正确用法比较起来,几乎没有什么不同: 

let myMap = new Map()
myMap.set('bla','blaa')
myMap.set('bla2','blaa2')
console.log(myMap)  // Map { 'bla' => 'blaa', 'bla2' => 'blaa2' }

myMap.has('bla')    // true
myMap.delete('bla') // true
console.log(myMap)  // Map { 'bla2' => 'blaa2' }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章