尷尬,通篇使用 if

以給部落格園頭部導航條鏈接添加圖標為例, 接下來看看如何分別使用對象、數組、Map 優化它的。

前置

1.接下來給頭部導航條添的圖標包含:

  • 部落格園首頁
  • 新隨筆
  • 部落格首頁
  • 聯繫
  • 訂閱
  • 管理

2.這裡封裝了一個返回 svg 的 function, 下文的 iconInSvg(icon) 即是調用了這個方法。

function iconInSvg(icon) {      return `              <svg class="icon" aria-hidden="true">                  <use xlink:href="${icon}"></use>              </svg>                `  }  

用if羅列

部落格園可以在 管理 -> 選項 介面禁用一些鏈接, 所以在添加 svg 圖標之前需要判斷鏈接是否存在。

import { cnblog, home, pens, contact, rss, admin } from '../constants/icons'  import { iconInSvg } from '@tools'    function setNavIcons() {      // 部落格園首頁      if ($('#blog_nav_sitehome').length !== 0) {          $('#blog_nav_sitehome').prepend(iconInSvg(cnblog))      }      // 部落格首頁      if ($('#blog_nav_myhome').length !== 0) {          $('#blog_nav_myhome').prepend(iconInSvg(home))      }      // 新隨筆      if ($('#blog_nav_newpost').length !== 0) {          $('#blog_nav_newpost').prepend(iconInSvg(pens))      }      // 聯繫      if ($('#blog_nav_contact').length !== 0) {          $('#blog_nav_contact').prepend(iconInSvg(contact))      }      // 訂閱      if ($('#blog_nav_rss').length !== 0) {          $('#blog_nav_rss').prepend(iconInSvg(rss))      }      // 管理      if ($('#blog_nav_admin').length !== 0) {          $('#blog_nav_admin').prepend(iconInSvg(admin))      }  }    export default setNavIcons    

可以發現程式碼有大量重複,思考如何優化程式碼。將選擇器和圖標抽離,以 key value 的形式一一對應,然後進行遍歷,執行相同的邏輯:先判斷再插入圖標。

使用對象

const items = {      '#blog_nav_sitehome': cnblog,      '#blog_nav_myhome': home,      '#blog_nav_newpost': pens,      '#blog_nav_contact': contact,      '#blog_nav_rss': rss,      '#blog_nav_admin': admin,  }    for (let selector in items) {      if ($(selector).length !== 0) {          $(selector).prepend(iconInSvg(items[selector]))      }  }  
  • js的對象本質上是鍵值對的集合(Hash結構),如果給對象設置為一個不為字元串的 key,會自動轉為字元串
  • 這裡直接設置 key 為字元串,需要使用[]來取其值

使用數組

針對給頭部導航條添加圖標的場景,使用對象重寫這段程式碼應該是最方便的解法了。接下來使用數組對象的形式將它重寫,並進行對比。

const items = [      { selector: '#blog_nav_sitehome', icon: cnblog },      { selector: '#blog_nav_myhome', icon: home },      { selector: '#blog_nav_newpost', icon: pens },      { selector: '#blog_nav_contact', icon: contact },      { selector: '#blog_nav_rss', icon: rss },      { selector: '#blog_nav_admin', icon: admin },  ]    for (let { selector, icon } of items) {      if ($(selector).length !== 0) {          $(selector).prepend(iconInSvg(icon))      }  }  
  • 數組對象的形式描述的更加清晰
  • 在這種數據結構下,通過結構賦值能夠快速取到值
  • 循環體內的程式碼更易讀

相比後面的 Map 數據結構,在這個場景下我更喜歡用數組對象的形式。

試試 Map

由於 js 對象的 key 本質上只能使用字元串,這就帶來一定的局限性。Map 結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。「鍵」的範圍不限於字元串,各種類型的值(包括對象)都可以當作鍵。接下來使用 Map 重寫這段程式碼。

const items = new Map([      ['#blog_nav_sitehome', cnblog],      ['#blog_nav_myhome', home],      ['#blog_nav_newpost', pens],      ['#blog_nav_contact', contact],      ['#blog_nav_rss', rss],      ['#blog_nav_admin', admin],  ])    for (let [selector, icon] of items.entries()) {      if ($(selector).length !== 0) {          $(selector).prepend(iconInSvg(icon))      }  }  

這個小項目雖然使用了 babel 來處理 es6,但 Map 無法像class那樣被轉化,class 是語法糖。Map 真是個好東西,如果你不需要兼容IE9以下的話。

總結

各有好處。需要注意的是,for of的效率沒有帶索引的遍歷方式高,當你需要遍歷大量數據時,最好換用其它方式遍曆數據。如有錯誤,歡迎指正!