<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          「 Map最佳實踐」什么時候適合使用 Map 而不是 Object

          共 6070字,需瀏覽 13分鐘

           ·

          2020-07-28 12:19

          a6e708777ea1ac9e048aa61b511676af.webp

          首先我們先有請「Map」簡單介紹下自己2de5a3bd13b55a738f19c9647b059861.webp524ecdec466a4eafb7e6986dc9516d07.webp「Map」映射是一種經(jīng)典的數(shù)據(jù)結(jié)構(gòu)類型,其中數(shù)據(jù)以 「key/value」 的鍵值對形式存在


          MapObject
          默認(rèn)值默認(rèn)不包含任何值,只包含顯式插入的鍵一個 Object 有一個原型,原型上的鍵名有可能和自己對象上設(shè)置的鍵名沖突
          類型任意StringSymbol
          長度鍵值對個數(shù)通過 size 屬性獲取鍵值對個數(shù)只能手動計算
          性能頻繁增刪鍵值對的場景下表現(xiàn)更好頻繁添加和刪除鍵值對的場景下未作出優(yōu)化

          Map 基本用法

          接受任何類型的鍵 劃重點,是任何 any!!!

          89916323e27dd46723832c0eac658d11.webp
          const?testMap?=?new?Map()

          let?str?=?'今天不學(xué)習(xí)',
          ????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,?'你的函數(shù)寫的好棒棒哦')
          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鍵名并無區(qū)別
          testMap.get(NaN)?//"我是個NaN"
          testMap.get(Number('NaN'))?//"我是個NaN"

          除了NaN比較特殊外,其他「Map」get方法都是通過對比鍵名是否相等(===)來獲取,不相等則返回undefined

          比較 Map 和 Object

          定義

          //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」到底在何時使用才是最佳實踐,別急接著來。

          鍵名類型

          JavaScript 「Object」只接收兩種類型的鍵名 StringSymbol,你可以使用其他類型的鍵名,但是最終 JavaScript 都會隱式轉(zhuǎn)換為字符串

          const?obj?=?{}
          //直接看幾種比較特殊的鍵名
          obj[true]?=?'Boolean'
          obj[1]?=?'Number'
          obj[{'前端':'Sneaker'}]?=?'666'

          Object.keys(obj)?//?["1",?"true",?"[object?Object]"]

          再來看看 「Map」 的,其接收任何類型的鍵名并保留其鍵名類型 (此處簡單舉例,詳細(xì)可看文章開頭「Map」基本使用)

          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}$/,'手機(jī)號正則')
          //Map(1)?{/^1[3456789]\d{9}$/?=>?"手機(jī)號正則"}

          「Map」支持正則表達(dá)式作為鍵名,這在Object是不被允許的直接報錯

          原型 Prototype

          「Object」不同于「Map」,它不僅僅是表面所看到的。「Map」只包含你所定義的鍵值對,但是「Object」對象具有其原型中的一些內(nèi)置屬性

          const?newObject?=?{};
          newObject.constructor;?//???Object()?{?[native?code]?}

          如果操作不當(dāng)沒有正確遍歷對象屬性,可能會導(dǎo)致出現(xiàn)問題,產(chǎn)生你意料之外的 bug77584286b46178c47ccb4345b631c86c.webpb60ed040350f8f1ecb287f62c60ca2ba.webp

          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]一書

          迭代器

          「Map」 是可迭代的,可以直接進(jìn)行迭代,例如forEach循環(huán)或者for...of...循環(huán)

          //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」是不能直接迭代的,當(dāng)你嘗試迭代將導(dǎo)致報錯435511f969a6427f8385b17f3e5972bd.webp

          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"

          當(dāng)然也可以使用for...in...進(jìn)行遍歷循環(huán)鍵名

          for(const?key?in?object)?{
          ??console.log(key);
          }
          //?key1
          //?key2
          //?key3

          元素順序和長度

          Map 保持對長度的跟蹤,使其能夠在O(1)復(fù)雜度中進(jìn)行訪問

          const?map?=?new?Map();
          map.set('key1',?'value1');
          map.set('key2',?'value2');
          map.set('key3',?'value3');
          map.size;?//?3

          而另一方面,對于「Object」而言,想要獲得對象的屬性長度,需要手動對其進(jìn)行迭代,使其為O(n)復(fù)雜度,屬性長度為n

          在上文提及的示例中,我們可以看到「Map」始終保持按插入順序返回鍵名。但「Object」卻不是。從 ES6 開始,StringSymbol鍵是按順序保存起來的,但是通過隱式轉(zhuǎn)換保存成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

          Object/Map 何為最佳實踐

          如上就是 「Map」「Object」 的基本區(qū)別,在解決問題考慮兩者的時候就需要考慮兩者的區(qū)別。

          • 當(dāng)插入順序是你解決問題時需要考慮的,并且當(dāng)前需要使用除 StringSymbol 以外的鍵名時,那么 「Map」 就是個最佳解決方案
          • 如果需要遍歷鍵值對(并且需要考慮順序),那我覺得還是需要優(yōu)先考慮 「Map」。
          • Map是一個純哈希結(jié)構(gòu),而Object不是(它擁有自己的內(nèi)部邏輯)。Map?在頻繁增刪鍵值對的場景下表現(xiàn)更好,性能更高。因此當(dāng)你需要頻繁操作數(shù)據(jù)的時候也可以優(yōu)先考慮 Map
          • 再舉一個實際的例子,比如有一個自定義字段的用戶操作功能,用戶可以通過表單自定義字段,那么這時候最好是使用 Map,因為很有可能會破壞原有的對象
          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
          • 當(dāng)你需要處理一些屬性,那么 「Object」 是完全受用的,尤其是需要處理 JSON 數(shù)據(jù)的時候。由于 「Map」 可以是任意類型,因此沒有可以將其轉(zhuǎn)化為 JSON 的原生方法。
          var?map?=?new?Map()
          map.set('key','value')
          JSON.stringify(map)??//"{}"
          • 當(dāng)你需要通正則表達(dá)式判斷去處理一些業(yè)務(wù)邏輯時,「Map」將是你的最佳解決方案
          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))
          }

          利用數(shù)組循環(huán)的特性,符合正則條件的邏輯都會被執(zhí)行,那就可以同時執(zhí)行公共邏輯和單獨(dú)邏輯,因為正則的存在,你可以打開想象力解鎖更多的玩法,更多相關(guān) Map 用法樣例可以查看JavaScript 復(fù)雜判斷的更優(yōu)雅寫法

          總結(jié):

          「Object」對象通??梢院芎玫谋4娼Y(jié)構(gòu)化數(shù)據(jù),但是也有相應(yīng)的局限性:

          1. 鍵名接受類型只能用 String 或者 Symbol
          2. 自定義的鍵名容易與原型繼承的屬性鍵名沖突(例如 toString,constructor 等)
          3. 對象/正則無法用作鍵名而這些問題通過 「Map」 都可以解決,并且提供了諸如迭代器和易于進(jìn)行大小查找之類的好處

          不要將「Map」作為普通「Object」的替代品,而應(yīng)該是普通對象的補(bǔ)充


          參考資料

          [1]

          《Effective TypeScript》Dan Vanderkam

          [2]

          https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Map

          [3]

          https://dmitripavlutin.com/maps-vs-plain-objects-javascript

          [4]

          https://medium.com/javascript-in-plain-english


          ?? 看完三件事

          如果你覺得這篇內(nèi)容對你挺有啟發(fā),我想邀請你幫我三個小忙:


          1. 點個「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點在看,都是耍流氓 -_-)

          2. 關(guān)注我的官網(wǎng)?https://muyiy.cn,讓我們成為長期關(guān)系

          3. 關(guān)注公眾號「高級前端進(jìn)階」,公眾號后臺回復(fù)「面試題」 送你高級前端面試題,回復(fù)「加群」加入面試互助交流群


          》》面試官都在用的題庫,快來看看《《

          瀏覽 24
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  俺也去在线视频 | 成人精品人妻 | 国产三级理论在线观看 | 中国10一14毛片 | 天天干视频在线 |