JavaScript 普通對象 {key:'value'} 可用於保存結構化數據數組
可是我發現很煩人的一件事,對象的鍵必須是字符串(或者不多使用的符號)。函數
若是用數字做爲鍵會怎麼樣呢?在這種狀況下沒有錯誤:ui
const names = {
1: 'One',
2: 'Two'
}
Object.keys(names); // ['1','2']
複製代碼
JavaScript 只是對象的鍵隱式轉換爲字符串。這是一件棘手的事情,由於你失去了類型的一致性。在本文中,我將介紹ES2015中提供的JavaScriptMap
對象如何解決許多普通對象的問題,包括將鍵轉換爲字符串。spa
如上所述,若是對象的鍵不是字符串或者符號,則js會將其隱式轉換爲字符串。 幸運的是,map在鍵類型上不存在問題:code
const numbersMap = new Map();
numbersMap.set(1,'one');
numbersMap.set(2,'two');
[...numbersMap.key()] // => [1,2]
複製代碼
1 和 2 是numbersMap 中的鍵,這些鍵的類型number保持不變 你能夠在map中使用任何鍵類型:數字,布爾以及經典的字符串和符號。對象
const booleansMap = new Map();
booleansMap.set(true,'Yep');
booleansMap.set(false,'Nope');
[...booleansMap.key()] // [true,fasle]
複製代碼
booleansMap 用布爾值做爲鍵沒有問題。繼承
一樣,布爾鍵在普通對象中不起做用。 讓咱們超越解界限:你能把整個對象用做map中的鍵嗎? 固然能夠!ip
假設你須要存儲一些與對象相關的數據,可是不能把這些數據附加到對象自己。內存
不能用普通對象這樣作。字符串
一種解決辦法就是用一組對象值元組:
const foo = {name:'foo'}
const bar = {name:'bar'}
const kindOfMap = [
[foo,'Foo related data'],
[bar,'Bar related data']
]
複製代碼
kindOfMap 是一個包含一對對象和關聯值的數組。
這種方式的最大的問題在於經過鍵訪問值的時間複雜度是O(n),必須遍歷整個數組才能經過鍵得到所須要的值
function getByKey(kindOfMap,key){
for(const [k,v] of kindOfMap){
if(key === k){
return v
}
}
return undefined
}
getByKey(kindOfMap,foo); // 'Foo related data'
複製代碼
使用 WeakMap
(Map)的專用版本,無需爲此煩惱,它接收把對象做爲鍵。
Map 和 WeakMap 之間的主要區別是後者容許對做爲鍵的對象進行垃圾回收,從而防止內存泄漏。
把上面的代碼重構使用WeakMap
的代碼付出的代價微不足道
const foo = {name: 'foo'}
const bar = {name: 'bar'}
const mapOfObject = new WeakMap();
mapOfObject.set(foo,'Foo related data')
mapOfObject.set(bar,'Bar related data')
mapOfObject.get(foo) // => 'Foo related data'
複製代碼
與Map 相對,WeapMap 僅僅接受把對象做爲鍵。
JavaScript中的任何對象都從其原型對象繼承屬性。普通的JavaScript 對象也是如此。
若是覆蓋從原型繼承的屬性,則可能會破壞依賴於這些原型屬性的代碼;
function isPlainObject(value){
return value.toString() === '[object Object]'
}
const actor = {
name: 'Harrison Ford',
toString: 'Actor: Harrison Ford'
}
// Does not work!
isPlainObject(actor); // TypeError: value.toString is not a function
複製代碼
在對象 actor 上定義的屬性 toString
覆蓋了從原型上面繼承的 toString() 方法,由於它依賴於toString()
方法,因此這破壞了 isObject()
;
檢查普通對象從原型繼承的屬性和方法列表 要避免使用這些名稱定義自定義屬性。
Map 沒有這個限制鍵的名稱不受限制:
function isMap(value){
return value.toSring() === '[object Map]'
}
const actorMap = new Map();
actorMap.set('name', 'Harrison Ford');
actorMap.set('toString', 'Actor: Harrison Ford');
// Works!
isMap(actorMap); // => true
複製代碼
無論 actorMap
是否具備 名稱爲 toString的屬性,方法 toString() 都能正常工做。
爲了遍歷普通對象的屬性,你必須使用其餘的輔助的靜態函數 例如object.key()或者Object.entries()
const colorsHex = {
'white': '#FFFFFF',
'black': '#000000'
};
for (const [color, hex] of Object.entries(colorsHex)) {
console.log(color, hex);
}
// 'white' '#FFFFFF'
// 'black' '#000000'
複製代碼
Object.entries(colorsHex)
返回從對象提取的鍵值對數組。
可是map 自己就是能夠迭代的:
const colorsHexMap = new Map();
colorsHexMap.set('white', '#FFFFFF');
colorsHexMap.set('black', '#000000');
for (const [color, hex] of colorsHexMap) {
console.log(color, hex);
}
// 'white' '#FFFFFF'
// 'black' '#000000'
複製代碼
colorsHexMap
是能夠迭代的,你能夠在任何可迭代的地方使用它:for()
循環、展開運算符[...map]
等。
map
還提供了返回迭代的其餘方法,map.key()
遍歷鍵 map.values()
遍歷值。
普通對象的另外一個問題是你沒法輕鬆肯定其擁有的屬性數量:
const exams = {
'John Smith': '10 points',
'Jane Doe': '8 points',
};
Object.keys(exams).length; // => 2
複製代碼
要肯定 exams 的大小,你必須經過它全部鍵來肯定它們的數量。
map 提供了一種替代方法,經過它的訪問器屬性 size 計算鍵值對:
const examsMap = new Map([
['John Smith', '10 points'],
['Jane Doe', '8 points'],
]);
examsMap.size; // => 2
複製代碼
普通的 JavaScript 對象一般能夠很好地保存結構化數據。可是它們有一些限制:
全部這些問題均可以經過 map 輕鬆解決。並且它們提供了諸如迭代器和易於進行大小查找之類的好處。
不要將 map 視爲普通對象的替代品,而應視爲補充。