合适首先请Map简单介绍一下自己。Map映射是一种经典的数据结构类型,数据以key/value键值对的形式存在MapObject的默认值默认不包含任何值,只包含显式插入的键。一个对象有一个原型。原型上的键名可能与自身对象上设置的键名冲突。传入任意String或Symbol长度的key-value对的个数。size属性只能通过人工计算得到key-value对的个数。在频繁增删键值对的场景下性能更好。对于频繁增删键值对的场景,没有优化。Map的基本用法接受任何类型的键。any!!!consttestMap=newMap()letstr='今天不学习',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,'前端运动鞋')testMap.set(keyFunction,'你的函数写的真好')testMap.set(keySymbol,'大前端')testMap.set(keyNull,'我是一个Null')testMap.set(keyUndefined,'我是一个Undefined')testMap.set(keyNaN,'我是一个NaN')testMap.get(function(){})//undefinedtestMap.get(Symbol('Web'))//undefined//虽然是NaN!==NaN,但是作为Map的键名没有区别testMap.get(NaN)//"我是NaN"testMap.get(Number('NaN'))//"我是NaNNaN"除了NaN比较特殊,其他Map的get方法都是通过比较key名是否为等于(_===_)得到,不等于返回undefined比较Map和Object定义//Mapconstmap=newMap();map.set('key','value');//Map(1){"key"=>"value"}map.get('key');//'value'//ObjectconstsomeObject={};someObject.key='value';someObject.key;//'value'here可以清楚地看到定义行为非常相似。看到这里你肯定没看到什么时候使用Map是最佳实践。不要担心键名类型。JavaScript对象只接受两种类型的键。NamesString和_Symbol_,可以使用其他类型的键名,但最终JavaScript会隐式转换为字符串constobj={}//直接看几个特殊的键名obj[true]='Boolean'obj[1]='Number'obj[{'Frontend':'Sneaker'}]='666'Object.keys(obj)//["1","true","[objectObject]"]我们再看看Map,接受任意类型的键名并保留其键名类型(这里是一个简单的例子,详见文章开头Map的基本使用)constmap=newMap();map.set(1,'value');map.set(true,'value');map.set({'key':'value'},'value');for(constkeyofmap.keys()){console.log(key);}//1//true//{key:"value"}//另外,Map也支持正则表达式作为键名map.set(/^1[3456789]\d{9}$/,'手机号Regular')//Map(1){/^1[3456789]\d{9}$/=>"手机号正则"}Map支持正则表达式作为键名,在Object中是不允许的,直接报一个error原型PrototypeObject与Map不同,不仅仅是表面上的样子。Map只包含你定义的键值对,但是Object对象在它的原型中有一些内置的属性constnewObject={};newObject.constructor;//?Object(){[nativecode]}如果没有正确遍历Object属性,这可能会导致问题并产生意想不到的错误constcountWords=(words)=>{constcounts={};for(constwordofwords){counts[word]=(counts[word]||0)+1;}返回计数;};constcounts=countWords(['constructor','creates','a','bug']);//{构造函数:"functionObject(){[nativecode]}1",creates:1,a:1,bug:1}这个例子是受本书的启发《Effective TypeScript》IteratorMap是可迭代的,可以直接迭代,比如forEach循环或者for..._of_..loop//forEachconstmap=newMap();map.set('key1','value1');map.set('key2','value2');map.set('key3','value3');map.forEach((value,key)=>{console.log(key,value);});//key1value1//key2value2//key3value3//for...of...for(constentryofmap){console.log(entry);}//["key1","value1"]//["key2","value2"]//["key3","value3"]但是对于Object是不能直接迭代的。尝试迭代时会报错constobject={key1:'value1',key2:'value2',key3:'value3',};for(constentryofobject){console.log(entry);}//UncaughtTypeError:objectisnotiterable这时候你需要额外的一步来获取它的键名,键值或者键值对for(constkeyofObject.keys(object)){console.log(key);}//key1//key2//key3for(Object.values(object)){console.log(value);}//value1//value2//value3for(Object.entries(object)的常量条目){console.log(entry);}//["key1","value1"]//["key2","value2"]//["key3","value3"]for(const[key,value]ofObject.entries(object)){console.log(key,value);}//"key1","value1"//"key2","value2"//"key3","value3"当然也可以用for..._in_...遍历循环keynamefor(constkeyinobject){console.log(key);}//key1//key2//key3元素顺序和长度Map跟踪长度,以便可以在O(1)复杂度内访问它constmap=newMap();map.set('key1','value1');map.set('key2','value2');map.set('key3','value3');map.size;//3另一方面,对于Object来说,如果要获取对象的属性长度,需要手动设置它迭代,使其复杂度为O(n),属性长度为n。在上面提到的示例中,我们可以看到Map总是按插入顺序返回键,而Object则不然。从ES6开始,String和Symbol的key是按顺序存储的,但是通过隐式转换保存成String的key是乱序??的constobject={};object['key1']='value1';object['key0']='值0';对象;//{key1:"value1",key0:"value0"}object[20]='value20';object;//{20:"value20",key1:"value1",key0:"value0"}Object.keys(object).length;//3Object/Map应用场景以上就是Map和Object的基本区别。在解决问题的时候,需要考虑两者的区别。当插入顺序是你解决问题时需要考虑的,而你目前需要使用除String和Symbol之外的键名,那么Map是最好的解决方案。如果需要遍历键值对(并且需要考虑顺序),那么我觉得还是需要优先考虑Map。Map是纯哈希结构,而Object不是(它有自己的内部逻辑)。_Map_在键值对频繁增删改查的场景下表现更好,性能更高。所以,当你需要频繁操作数据时,也可以优先考虑Map,再举一个实际的例子。比如有自定义字段用户操作功能,用户可以通过表单自定义字段。那么此时最好使用Map,因为它极有可能破坏原有对象constuserCustomFields={'color':'blue','size':'medium','toString':'Abluebox'};这时候自定义的toString会销毁为原来的对象,Map的key接受任意类型,没有效果functionisMap(value){returnvalue.toString()==='[objectMap]';}constactorMap=newMap();actorMap.set('name','HarrisonFord');actorMap.set('toString','Actor:HarrisonFord');//有效!isMap(actorMap);//=>true当你需要处理一些属性时,那么Object就完全有用了,尤其是在处理JSON数据的时候。由于Map可以是任何类型,因此没有本地方法可以将其转换为JSON。varmap=newMap()map.set('key','value')JSON.stringify(map)//"{}"如果需要在对象中保持唯一的逻辑和属性,只能使用Objectvarobj={id:1,name:"FrontSneaker",speak:function(){返回`ObjectId:${this.id},withName:${this.name}`;}}console.log(obj.speak());//ObjectId:1,withName:front-endSneaker.当你需要通过正则表达式判断来处理一些业务逻辑时,Map将是你最好的解决方案constactions=()=>{constfunctionA=()=>{/*dosth*/}constfunctionB=()=>{/*做某事*/}constfunctionC=()=>{/*发送日志*/}returnnewMap([[/^guest_[1-4]$/,functionA],[/^guest_5$/,functionB],[/^guest_.*$/,functionC],//...])}constonButtonClick=(identity,status)=>{让action=[...actions()].filter(([key,value])=>(key.test(`${identity}_${status}`)))action.forEach(([key,value])=>value.call(this))}利用的特性数组循环,满足正则条件的逻辑会被执行,那么公共逻辑和个别逻辑可以同时执行,因为正则的存在,可以开启想象解锁更多玩法,更多相关Map的使用示例可以在JavaScript中找到复杂判断的写法更优雅可以通过String或Symbol自定义的键名,方便与原文匹配继承属性键名冲突(如toString_、_constructor等)objects/regulars不能作为键名,这些问题可以通过Map来解决,并提供iterators和easysizelookup等好处。不要将Map作为普通对象的替代品,而是普通对象的补充。喜欢小编的可以关注下方公众号了解更多干货
