<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 而不是 Object ?學到了~

          共 8321字,需瀏覽 17分鐘

           ·

          2021-07-27 22:00


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


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

          Map 基本用法

          接受任何類型的鍵 劃重點,是任何 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, '你的函數(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」 的,其接收任何類型的鍵名并保留其鍵名類型 (此處簡單舉例,詳細可看文章開頭「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}$/,'手機號正則')
          //Map(1) {/^1[3456789]\d{9}$/ => "手機號正則"}

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

          原型 Prototype

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

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

          如果操作不當沒有正確遍歷對象屬性,可能會導致出現(xiàn)問題,產(chǎn)生你意料之外的 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]一書

          迭代器

          「Map」 是可迭代的,可以直接進行迭代,例如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」是不能直接迭代的,當你嘗試迭代將導致報錯

          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...進行遍歷循環(huán)鍵名

          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 開始,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ū)別。

          • 插入順序是你解決問題時需要考慮的,并且當前需要使用除 StringSymbol 以外的鍵名時,那么 「Map」 就是個最佳解決方案
          • 如果需要遍歷鍵值對(并且需要考慮順序),那我覺得還是需要優(yōu)先考慮 「Map」
          • Map是一個純哈希結(jié)構(gòu),而Object不是(它擁有自己的內(nèi)部邏輯)。Map 在頻繁增刪鍵值對的場景下表現(xiàn)更好,性能更高。因此當你需要頻繁操作數(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
          • 當你需要處理一些屬性,那么 「Object」 是完全受用的,尤其是需要處理 JSON 數(shù)據(jù)的時候。由于 「Map」 可以是任意類型,因此沒有可以將其轉(zhuǎn)化為 JSON 的原生方法。
          var map = new Map()
          map.set('key','value')
          JSON.stringify(map)  //"{}"
          • 當你需要通正則表達式判斷去處理一些業(yè)務邏輯時,「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í)行公共邏輯和單獨邏輯,因為正則的存在,你可以打開想象力解鎖更多的玩法,更多相關 Map 用法樣例可以查看JavaScript 復雜判斷的更優(yōu)雅寫法

          總結(jié):

          「Object」對象通常可以很好的保存結(jié)構(gòu)化數(shù)據(jù),但是也有相應的局限性:

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

          不要將「Map」作為普通「Object」的替代品,而應該是普通對象的補充


          參考資料

          [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. 歡迎加我微信「 sherlocked_93 」拉你進技術(shù)群,長期交流學習...

          3. 關注公眾號「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時聊騷。


          點個在看支持我吧,轉(zhuǎn)發(fā)就更好了



          瀏覽 33
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  国产精品卡_卡2卡3卡4一商战-信息网 | 欧美精品在线播放 | 超碰AV无码 | 鸡巴操穴视频 | 97人人干|