停止在JavaScript中將對象用作哈希映射
Map是日常編程中比較常用的數(shù)據(jù)結(jié)構(gòu)之一。它保留了可以很容易地通過其鍵來訪問的鍵值對。在Java中,使用HashMap來實現(xiàn)這個目的是很明顯的。然而,在JavaScript中,使用一個普通的對象來實現(xiàn)這個目標是非常方便的。
- const map = {};
- // 插入鍵值對
- map['key1'] = 'value1';
- map['key2'] = 'value2';
- map['key3'] = 'value3';
- // 檢查map包含的鍵
- if (map.hasOwnProperty('key1')) {
- console.log('Map contains key1');
- }
- // 通過特定的鍵獲得值
- console.log(map['key1']);
但是JavaScript中有一個專門用于此目的的內(nèi)置數(shù)據(jù)結(jié)構(gòu):Map。讓我給你一些理由,讓你喜歡Map而不是普通的對象。
1.更多鍵類型
對象只能有符號(symbols)或字符串。Map可以將任何類型的值作為鍵:對象,函數(shù)或基元(primitives)。
- const map = new Map();
- const myFunction = () => console.log('I am a useful function.');
- const myNumber = 666;
- const myObject = {
- name: 'plainObjectValue',
- otherKey: 'otherValue'
- };
- map.set(myFunction, 'function as a key');
- map.set(myNumber, 'number as a key');
- map.set(myObject, 'object as a key');
- console.log(map.get(myFunction)); // function as a key
- console.log(map.get(myNumber)); // number as a key
- console.log(map.get(myObject)); // object as a key
2.更好地確定大小
Map提供了一個size屬性,但一個普通對象的大小卻必須通過艱難的方式來確定。確定Map的大小可以在O(1)時間內(nèi)完成,而確定一個普通對象的大小則需要O(n)步。
- const map = new Map();
- map.set('someKey1', 1);
- map.set('someKey2', 1);
- ...
- map.set('someKey100', 1);
- console.log(map.size) // 100, Runtime: O(1)
- const plainObjMap = {};
- plainObjMap['someKey1'] = 1;
- plainObjMap['someKey2'] = 1;
- ...
- plainObjMap['someKey100'] = 1;
- console.log(Object.keys(plainObjMap).length) // 100, Runtime: O(n)
3.更好的性能
對Map進行了優(yōu)化,以便頻繁地添加和刪除條目。
此外,Map的條目數(shù)可以在恒定的時間內(nèi)被檢索,而一個普通對象的條目數(shù)必須被計算,這需要O(n)時間。
以我的Macbook Pro為例,這是一張有1000萬個條目的Map的平均大小確定時間。
- 普通JS對象: ~1.6 s
- Map: < 1 ms
此外,它不需要將任何鍵轉(zhuǎn)換為字符串,這可以節(jié)省很多時間。
4.直接迭代
對象必須通過獲取鍵并對其進行迭代。另一方面,Map是可迭代的,這意味著它可以直接迭代。
- const map = new Map();
- map.set('someKey1', 1);
- map.set('someKey2', 2);
- map.set('someKey3', 3);
- for (let [key, value] of map) {
- console.log(`${key} = ${value}`);
- }
- // someKey1 = 1
- // someKey2 = 2
- // someKey3 = 3
- const plainObjMap = {};
- plainObjMap['someKey1'] = 1;
- plainObjMap['someKey2'] = 2;
- plainObjMap['someKey3'] = 3;
- for (let key of Object.keys(plainObjMap)) {
- const value = plainObjMap[key];
- console.log(`${key} = ${value}`);
- }
- // someKey1 = 1
- // someKey2 = 2
- // someKey3 = 3
5.key順序
在ECMAScript 2015之前,一個對象的鍵不保證以任何特定的順序出現(xiàn)。在Map上迭代保證鍵按插入順序出現(xiàn)。
6.無鍵覆蓋
一個普通對象由于其原型已經(jīng)包含了一些鍵,你的鍵和對象已經(jīng)包含的鍵之間可能會有沖突。Map在創(chuàng)建時不包含任何鍵。
注意:自ECMAScript 2015年起,你可以通過使用 Object.create(null) 來創(chuàng)建你的普通對象圖來避免意外的鍵覆蓋。
- const map = new Map();
- map.set('someKey1', 1);
- map.set('someKey2', 2);
- map.set('toString', 3); // No problem for Map
- const plainObjMap = new Map();
- plainObjMap['someKey1'] = 1;
- plainObjMap['someKey2'] = 2;
- plainObjMap['toString'] = 3; // Oops, native property