lodash源碼分析之compact中的遍歷

来源:http://www.cnblogs.com/hefty/archive/2017/12/18/8055931.html
-Advertisement-
Play Games

小時候, 鄉愁是一枚小小的郵票, 我在這頭, 母親在那頭。 長大後,鄉愁是一張窄窄的船票, 我在這頭, 新娘在那頭。 後來啊, 鄉愁是一方矮矮的墳墓, 我在外頭, 母親在裡頭。 而現在, 鄉愁是一灣淺淺的海峽, 我在這頭, 大陸在那頭。 ——餘光中《鄉愁》 本文為讀 lodash 源碼的第三篇,後續 ...


小時候,

鄉愁是一枚小小的郵票,

我在這頭,

母親在那頭。

長大後,鄉愁是一張窄窄的船票,

我在這頭,

新娘在那頭。

後來啊,

鄉愁是一方矮矮的墳墓,

我在外頭,

母親在裡頭。

而現在,

鄉愁是一灣淺淺的海峽,

我在這頭,

大陸在那頭。

——餘光中《鄉愁》

本文為讀 lodash 源碼的第三篇,後續文章會更新到這個倉庫中,歡迎 star:pocket-lodash

gitbook也會同步倉庫的更新,gitbook地址:pocket-lodash

作用與用法

compact 函數用來去除數組中的假值,並返回由不為假值元素組成的新數組。

falsenull0""undefinedNaN 都為假值。

例如:

var arr = [1,false,2,null,3,0,4,NaN,5,undefined]
_.compact(arr) // 返回 [1,2,3,4,5]

源碼

function compact(array) {
  let resIndex = 0
  const result = []

  if (array == null) {
    return result
  }

  for (const value of array) {
    if (value) {
      result[resIndex++] = value
    }
  }
  return result
}

compact 的源碼只有寥寥幾行,相當簡單。

首先判斷傳入的數組是否為 null 或者 undefined,如果是,則返回空數組。

然後用 for...of 來取得數組中每項的值,如果不為假值,則存入新數組 result 中,最後將新數組返回。

到這裡,源碼分析完了。

但是在看源碼的時候,發現這裡用了 for...of 來做遍歷,其實除了 for...of 外,也可以用 for 或者 for...in 來做遍歷,那為什麼最後選了 for...of 呢?

數組中的for迴圈

使用 for 迴圈,很容易就將 compact 中關於迴圈部分的源碼改寫成以下形式:

for (let i = 0; i < array.length; i++) {
    const value = array[i]
    if (value) {
      result[resIndex++] = value
    }
  }

這樣寫,肯定是沒有問題的,但是數組不總是密集的,也有可能是稀疏數組,假如:var arr = [1,2,3,,4,,5] 這樣的稀疏數組,會出現2次無效的迴圈。

關於稀疏數組,可以看本系列的第一篇文章《讀lodash源碼之從slice看稀疏數組與密集數組》。

for…in

再來看 for...in 迴圈,先來將源碼改寫一下:

for (let index in array) {
  const value = array[i]
  if (value) {
    result[resIndex++] = value
  }
}

先看看MDN上關於 for...in 的用法:

for...in語句以任意順序遍歷一個對象的可枚舉屬性

關於可枚舉屬性,可以點擊上面的鏈接到MDN上瞭解一下,這裡不做太多的解釋。

在數組中,數組的索引是可枚舉屬性,可以用 for...in 來遍曆數組的索引,數組中的稀疏部分不存在索引,可以避免用 for 迴圈造成無效遍歷的弊端。

但是,for...in 有兩個致命的特性:

  1. for...in 的遍歷不能保證順序
  2. for...in 會遍歷所有可枚舉屬性,包括繼承的屬性。

for...in 的遍歷順序依賴於執行環境,不同執行環境的實現方式可能會不一樣。單憑這一點,就斷然不能在數組遍歷中使用 for...in,大多數情況下,順序對於數組的遍歷都相當重要。

關於第二點,先看個例子:

var arr = [1,2,3]
arr.foo = 'foo'
for (let index in arr) {
  console.log(index)
}

在這個例子中,你期望輸出的是 0,1,2,但是最後輸出的可能是 0,1,2,foofor...in 不能保證順序)。因為 foo 也是可枚舉屬性,在 for..in 會被遍歷出來。

for…of

最後來看看 for...of

當我們在控制臺中列印一個數組,並將它展開來查看時,會在數組的原型鏈上發現一個很特別的屬性 Symbol.iterator

其實 for...of 迴圈內部調用的就是數組原型鏈上的 Symbol.iterator 方法。

Symbol.iterator 在調用的時候會返回一個遍歷器對象,這個遍歷器對象中包含 next 方法,for...of 在每次迴圈的時候都會調用 next 方法來獲取值,直到 next 返回的對象中的 done屬性值為 true 時停止。

其實我們也可以手動調用來模擬遍歷的過程:

const arr = [1,2,3]
const iterator = a[Symbol.iterator]()
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {value: undefined, done: true}

知道這些原理後,完全可以改寫數組中的 Symbol.iterator 方法,例如遍歷時將數組中的值都乘2:

Array.prototype[Symbol.iterator] = function () {
  let index = 0
  const _self = this
  return {
    next: function () {
      if (index < _self.length) {
        return {value: _self[index++] * 2, done: false}
      } else {
        return {done: true}
      }
    }
  }
}

使用 Generator 函數可以寫成以下的形式:

Array.prototype[Symbol.iterator] = function* () {
  let index = 0
  while (index < this.length) {
    yield this[index++] * 2   
  }
}

因此在不改寫 Symbol.iterator 的情況下,使用 for...of 來遍曆數組是安全的,因為這個方法是數組的原生方法,而且使用 for...of 來遍歷同樣不會遍曆數組中稀疏數部分。

關於 IteratorGenerator 可以點擊參考中的鏈接詳細查看。

參考

  1. MDN:迭代器和生成器
  2. Iterator 和 for...of 迴圈
  3. Generator 函數的語法
  4. Lodash源碼講解(3)-compact函數
  5. MDN:for...of
  6. MDN:for…in

License

署名-非商業性使用-禁止演繹 4.0 國際 (CC BY-NC-ND 4.0)

最後,所有文章都會同步發送到微信公眾號上,歡迎關註,歡迎提意見:

作者:對角另一面


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第1章 keepalived服務說明 1.1 keepalived是什麼? Keepalived軟體起初是專為LVS負載均衡軟體設計的,用來管理並監控LVS集群系統中各個服務節點的狀態,後來又加入了可以實現高可用的VRRP功能。因此,Keepalived除了能夠管理LVS軟體外,還可以作為其他服務( ...
  • 測試系統:centos7 修改連接埠 修改配置文件 去掉port 22的註釋,添加新的埠配置 自定義埠選擇建議在萬位的埠(如:10000 65535之間) 不要直接刪除port 22,以免遺忘新SSH埠或者埠不能訪問,我們還能繼續訪問SSH,保存並退出 重啟SSH配置 重新連接VPS並刪 ...
  • 1.1 集群是什麼 簡單地說,集群就是指一組(若幹個)相互獨立的電腦,利用高速通信網路組成的一個較大的電腦服務系統,每個集群節點(即集群中的每台電腦)都是運行各自服務的獨立服器。這些伺服器之間可以彼此通信,協同向用戶提供應用程式、系統資源和數據,並以單一系統的模式加以管理。當用戶客戶機請求集群 ...
  • 1、Flume概念 flume是分散式日誌收集系統,將各個伺服器的數據收集起來併發送到指定地方。 Flume是Cloudera提供的一個高可用、高可靠、分散式的海量日誌採集、聚合和傳輸的系統。Flume支持在日誌系統中定製各類數據發送方,用於收集數據;同時,Flume提供對數據進行簡單處理,並寫到各 ...
  • 一、什麼是序列 序列是用於生成唯一、連續序號的對象。序列可以是升序的,也可以是降序的,Oracle用戶想創建序列必須有創建序列的角色許可權。 二、創建序列的SQL語法 創建序列:CREATE SEQUENCE SEQ_NAME START WITH n INCREMENT BY n2 MAXVALUE ...
  • 本文是學習時的自我總結,用於日後溫習。如有錯誤還望諒解,不吝賜教。 此處附上一篇個人認為寫的比較好的博客,轉自枝葉飛揚的博文:http://blog.sina.com.cn/s/blog_605f5b4f010188lp.html### 將Map的輸出作為Reduce的輸入的過程就是Shuffle了 ...
  • mysql每次建立一個socket連接(connect)時,這個socket都會占用一定記憶體。即使你關閉(close)連接時,並不是真正的關閉,而是處於睡眠(sleep)狀態。 當你下次再進行連接時,就可以快速啟動當前處於睡眠狀態的socket。但是過多的socket會占用大量的記憶體,為解決這個問題 ...
  • 一,定位服務。 iOS設備能提供3種不同的定位途徑: 1,WiFi定位,通過查詢一個WiFi路由器的地理位置的信息,比較省電;iPhone,Ipod touch 和iPad都可以。 2,蜂窩式行動電話基站定位,通過移動運營商基站定位,只有iPhone,3G版本的iPod touch和iPad可以採用 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...