首先咱們先有請「Map」簡單介紹下本身 「Map」映射是一種經典的數據結構類型,其中數據以 「key/value」 的鍵值對形式存在javascript
Map | Object | |
---|---|---|
默認值 | 默認不包含任何值,只包含顯式插入的鍵 | 一個 Object 有一個原型,原型上的鍵名有可能和本身對象上設置的鍵名衝突 |
類型 | 任意 | String 或 Symbol |
長度 | 鍵值對個數經過 size 屬性獲取 | 鍵值對個數只能手動計算 |
性能 | 頻繁增刪鍵值對的場景下表現更好 | 頻繁添加和刪除鍵值對的場景下未做出優化 |
接受任何類型的鍵 劃重點,是任何 any!!!前端
const testMap = new Map()
let str = '今天不學習', num = 666, keyFunction = function () {}, keySymbol = Symbol('Web'), keyNull = null, keyUndefined = undefined, keyNaN = NaN //添加鍵值對 //基本用法 testMap.set('key', 'value') // Map(1) {"key" => "value"} testMap.set(str, '明天變辣雞') testMap.set(num, '前端Sneaker') testMap.set(keyFunction, '你的函數寫的好棒棒哦') testMap.set(keySymbol, '大前端') testMap.set(keyNull, '我是個Null') testMap.set(keyUndefined, '我是個Undifined') testMap.set(keyNaN, '我是個NaN') testMap.get(function () {}) //undefined testMap.get(Symbol('Web')) //undefined //雖然NaN !== NaN 可是做爲Map鍵名並沒有區別 testMap.get(NaN) //"我是個NaN" testMap.get(Number('NaN')) //"我是個NaN" 複製代碼
除了NaN比較特殊外,其餘「Map」的get方法都是經過對比鍵名是否相等(===)來獲取,不相等則返回undefinedjava
//Map
const map = new Map(); map.set('key', 'value'); // Map(1) {"key" => "value"} map.get('key'); // 'value' //Object const someObject = {}; someObject.key = 'value'; someObject.key; // 'value' 複製代碼
這裏能夠明顯看出其實其定義行爲是十分類似的,想必看到這裏你們還沒看出來「Map」到底在什麼時候使用纔是最佳實踐,別急接着來。web
JavaScript 「Object」只接收兩種類型的鍵名 String 和 Symbol,你可使用其餘類型的鍵名,可是最終 JavaScript 都會隱式轉換爲字符串正則表達式
const obj = {}
//直接看幾種比較特殊的鍵名 obj[true] = 'Boolean' obj[1] = 'Number' obj[{'前端':'Sneaker'}] = '666' Object.keys(obj) // ["1", "true", "[object Object]"] 複製代碼
再來看看 「Map」 的,其接收任何類型的鍵名並保留其鍵名類型 (此處簡單舉例,詳細可看文章開頭「Map」基本使用)typescript
const map = new Map();
map.set(1, 'value'); map.set(true, 'value'); map.set({'key': 'value'}, 'value'); for (const key of map.keys()) { console.log(key); } // 1 // true // {key: "value"} //除此以外,Map還支持正則做爲鍵名 map.set(/^1[3456789]\d{9}$/,'手機號正則') //Map(1) {/^1[3456789]\d{9}$/ => "手機號正則"} 複製代碼
「Map」支持正則表達式做爲鍵名,這在Object是不被容許的直接報錯數組
「Object」不一樣於「Map」,它不只僅是表面所看到的。「Map」只包含你所定義的鍵值對,可是「Object」對象具備其原型中的一些內置屬性數據結構
const newObject = {};
newObject.constructor; // ƒ Object() { [native code] } 複製代碼
若是操做不當沒有正確遍歷對象屬性,可能會致使出現問題,產生你意料以外的 bug 編輯器
const countWords = (words) => {
const counts = { }; for (const word of words) { counts[word] = (counts[word] || 0) + 1; } return counts; }; const counts = countWords(['constructor', 'creates', 'a', 'bug']); // {constructor: "function Object() { [native code] }1", creates: 1, a: 1, bug: 1} 複製代碼
這個例子靈感來源於《Effective TypeScript》[1]一書ide
「Map」 是可迭代的,能夠直接進行迭代,例如forEach循環或者for...of...循環
//forEach
const map = new Map(); map.set('key1', 'value1'); map.set('key2', 'value2'); map.set('key3', 'value3'); map.forEach((value, key) => { console.log(key, value); }); // key1 value1 // key2 value2 // key3 value3 //for...of... for(const entry of map) { console.log(entry); } // ["key1", "value1"] // ["key2", "value2"] // ["key3", "value3"] 複製代碼
可是對於「Object」是不能直接迭代的,當你嘗試迭代將致使報錯
const object = {
key1: 'value1', key2: 'value2', key3: 'value3', }; for(const entry of object) { console.log(entry); } // Uncaught TypeError: object is not iterable 複製代碼
這時候你就須要一個額外的步驟來檢索其鍵名、鍵值或者鍵值對
for(const key of Object.keys(object)) {
console.log(key); } // key1 // key2 // key3 for(const value of Object.values(object)) { console.log(value); } // value1 // value2 // value3 for(const entry of Object.entries(object)) { console.log(entry); } // ["key1", "value1"] // ["key2", "value2"] // ["key3", "value3"] for(const [key,value] of Object.entries(object)) { console.log(key,value); } //"key1", "value1" //"key2", "value2" //"key3", "value3" 複製代碼
固然也可使用for...in...進行遍歷循環鍵名
for(const key in object) {
console.log(key); } // key1 // key2 // key3 複製代碼
Map 保持對長度的跟蹤,使其可以在O(1)複雜度中進行訪問
const map = new Map();
map.set('key1', 'value1'); map.set('key2', 'value2'); map.set('key3', 'value3'); map.size; // 3 複製代碼
而另外一方面,對於「Object」而言,想要得到對象的屬性長度,須要手動對其進行迭代,使其爲O(n)複雜度,屬性長度爲n
在上文說起的示例中,咱們能夠看到「Map」始終保持按插入順序返回鍵名。但「Object」卻不是。從 ES6 開始,String和Symbol鍵是按順序保存起來的,可是經過隱式轉換保存成String的鍵就是亂序的
const object = { };
object['key1'] = 'value1'; object['key0'] = 'value0'; object; // {key1: "value1", key0: "value0"} object[20] = 'value20'; object; // {20: "value20", key1: "value1", key0: "value0"} Object.keys(object).length; //3 複製代碼
如上就是 「Map」 和 「Object」 的基本區別,在解決問題考慮二者的時候就須要考慮二者的區別。
const userCustomFields = {
'color': 'blue', 'size': 'medium', 'toString': 'A blue box' }; 複製代碼
此時用戶自定義的 toString 就會破壞到原有的對象 而 「Map」 鍵名接受任何類型,沒有影響
function isMap(value) {
return value.toString() === '[object Map]'; } const actorMap = new Map(); actorMap.set('name', 'Harrison Ford'); actorMap.set('toString', 'Actor: Harrison Ford'); // Works! isMap(actorMap); // => true 複製代碼
var map = new Map()
map.set('key','value') JSON.stringify(map) //"{}" 複製代碼
var obj = {
id: 1, name: "前端Sneaker", speak: function(){ return `Object Id: ${this.id}, with Name: ${this.name}`; } } console.log(obj.speak());//Object Id: 1, with Name: 前端Sneaker. 複製代碼
const actions = ()=>{
const functionA = ()=>{/*do sth*/} const functionB = ()=>{/*do sth*/} const functionC = ()=>{/*send log*/} returnnewMap([ [/^guest_[1-4]$/,functionA], [/^guest_5$/,functionB], [/^guest_.*$/,functionC], //... ]) } const onButtonClick = (identity,status)=>{ let action = [...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`))) action.forEach(([key,value])=>value.call(this)) } 複製代碼
利用數組循環的特性,符合正則條件的邏輯都會被執行,那就能夠同時執行公共邏輯和單獨邏輯,由於正則的存在,你能夠打開想象力解鎖更多的玩法,更多相關 Map 用法樣例能夠查看JavaScript 複雜判斷的更優雅寫法
「Object」對象一般能夠很好的保存結構化數據,可是也有相應的侷限性:
不要將「Map」做爲普通「Object」的替代品,而應該是普通對象的補充
若是喜歡小編,能夠關注下面的公衆號,瞭解更多幹貨。
Effective TypeScript: https://www.oreilly.com/library/view/effective-typescript/9781492053736/
[2]MDN: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map
[3]dmitripavlutin: https://dmitripavlutin.com/maps-vs-plain-objects-javascript
[4]medium: https://medium.com/javascript-in-plain-english