Map 是什么
看下 MDN 解释:
The Map object holds key-value pairs and remembers the original insertion order of the keys. Any value (both objects and primitive values) may be used as either a key or a value.
关键词:键值对、按插入顺序有序排列、键或者值可以为任何类型的数据。
语法
new Map([iterable])
我们再看一下 Map
实例上的属性和方法。
初始化一个 Map 实例:
const map = new Map(); // 空的 Map map.set(1, "first"); map.get(1); // 'first' const newMap = new Map([ [1, "zhangsan"], [2, "lisi"] ]); newMap.get(1); // "zhangsan" newMap.get(2); // "lisi"
键值
对象里的键值只能为字符串。而 Map
实例如上解释可以为任何类型的数据。
const obj = {}; obj[1] = "one"; obj["1"]; // one obj[1]; // one const map = new Map(); map.set(1, "one"); map.set("1", "another one"); // map 包含2组键值对: 1, 'one' 以及 '1', 'another one'
这里再说一下,Map
实例当在比较键名的时候采用 sameValueZero 的规则,有点像严格等于 ===
,但又有两个例外:NaN 和 NaN、+0 和-0,Map 认为它们一样。
另外,Map 实例还支持链式调用设置键值对,对象只能一次设置一个键值对:
const map = new Map(); map .set(1, "one") .set(2, "two") .set(3, "three");
删除属性
删除对象上的属性值时会一直返回 true,除非这个属性是
删除一个 Map
上的属性时,如果这个属性存在会返回 true,如果不存在则返回 false。
// 删除对象属性 const obj = { name: "zhangsan" }; delete obj.name; // true delete obj.age; // true,不存在这个属性同样会返回 true // 删除Map属性 const map = new Map(); map.set("name", "zhangsan"); map.delete("name"); // true map.delete("age"); // false
如果删除对象上所有的属性呢?或许可以这样:
const obj = { 1: 234 }; obj = {};
但是这样的话,你其实只是把一个新的空对象复制给这个实例,而原先的旧对象并不一定就被删除了(要看垃圾回收机制),如果别的地方对它还有引用,那它就还会一直存在。
for (let key in obj) { if (obj.hasOwnProperty(key)) { delete obj[key]; } }
Map
实例就能很好地处理这个问题:map.clear();
。
遍历
如开头 MDN
上的解释一样, Map
的遍历更好预测。
const obj = {}; obj[5] = "five"; obj[4] = "four"; Object.entries(obj); // [ ['4', 'four'], ['5', "five"] ] const map = new Map(); map .set(5, "five") .set(4, "four") .entries(); // [ 5 => "five", 4 => "four" ]
和对象类似,Map
也有 3 种方法用于遍历对象:
map.keys()
返回键名组成的数组
map.values()
返回键值组成的数组
map.entries()
返回包含一组组键值对的可遍历的数组
相互转换
Map
构造函数接收一个数组或者可迭代(iterable
)对象,所以对象转 Map
实例的时候我们可以用 Object.entries
方法处理:
const obj = { one: 1, two: 2 }; const map = new Map(Object.entries(obj)); console.log(map.get("one")); // 1
那么又如何把 Map
实例转成对象呢?还好我们有 Object.fromEntries
方法,用法和 Object.entries
相反:
const map = new Map(); map.set("one", 1); map.set("two", 2); const obj = Object.fromEntries(map); console.log(obj.one); // 1
Map vs WeakMap
WeakMap
与 Map
类似,主要的不同点有两处:
WeakMap
的只接受对象作为键名。
const map = new WeakMap(); map.set(1, 2); // TypeError: Invalid value used as weak map key map.set({}, 1);
WeakMap
键名所指向的对象,不计入垃圾回收机制。
let obj = { name: "Matt" }; // object can be accessed let map = new Map(); map.set(obj, true); obj = null; // 尽管对象被重写了,但是旧对象依然可以在map里访问到
WeakMap
是怎么处理这种情况的:
let obj = { name: "Matt" }; let weakMap = new WeakMap(); weakMap.set(obj, true); obj = null; // 重写对象,同时也会在 weakMap 里被删除 // weakMap 现在是空
WeakMap
只有四种方法:get
, set
, delete
, has
。 之所以没有 size
等属性或者方法,就是因为这个机制, WeakMap
实例的长度可能会随时变化,所以从规则上就禁止去访问 WeakMap
的 size
属性,自然也不会提供用于遍历的那几个属性了。
总结
从一些共同点以及不同点介绍了对象以及 Map
的特性,希望能帮助到你了解他们的优缺点,进而在适合的场景选用适合的数据结构。