經過閱讀源碼來提升js知識

經過閱讀源碼來提升js知識

原文傳送門:《Improve Your JavaScript Knowledge By Reading Source Code》javascript

本來做者:Carl Mungazi,是位於倫敦的能源創業公司Limejump的前端開發人員。他花時間深刻挖掘全部JavaScript的深度。html

簡介:當你還處於編程生涯的初期階段時,深刻研究開源庫和框架的源代碼多是一項艱鉅的任務。在這篇文章中,Carl Mungazi分享了他如何克服恐懼並開始使用源代碼來提升他的知識和技能。他還使用Redux來演示他如何破壞圖書館。

你還記得你第一次深刻挖掘常用的庫或框架的源代碼嗎?對我而言,那一刻是我三年前做爲前端開發人員的第一份工做。前端

咱們剛剛完成了用於建立電子學習課程的內部遺留框架的重寫。在重寫開始時,咱們花時間研究了許多不一樣的解決方案,包括Mithril,Inferno,Angular,React,Aurelia,Vue和Polymer。由於我是一個很是初學者(我剛重新聞轉向網絡開發),我記得每一個框架的複雜性讓人感到懼怕,而不是理解每一個框架的工做方式。java

當我開始更深刻地研究咱們選擇的框架Mithril時,個人理解增加了。從那之後,我對JavaScript的瞭解 - 以及通常的編程 - 獲得了很大的幫助,我花了不少時間深刻研究我天天在工做或我本身的項目中使用的庫的內容。在這篇文章中,我將分享一些您能夠採用本身喜歡的圖書館或框架並將其用做教育工具的方法。node

如下是我第一次閱讀代碼的介紹是經過Mithril的hyperscript函數react

*我第一次閱讀代碼的介紹是經過Mithril的hyperscript函數。*

閱讀源代碼的好處

閱讀源代碼的主要好處之一是增長了你能夠學習的知識數量。當我第一次看到Mithril的代碼庫時,我對虛擬DOM的含義有一個模糊的概念。當我學習完後,我知道虛擬DOM是一種技術,它涉及建立描述用戶界面應該是什麼樣的對象樹。而後使用DOM API將該樹轉換爲DOM元素document.createElement。經過建立描述用戶界面的將來狀態的新樹,而後將其與舊樹中的對象進行比較來執行更新。git

我在各類文章和教程中也都閱讀過這些內容,它對我頗有幫助,而且在咱們發佈的應用程序的中可以觀察它的運做,對我來講很是有啓發性。它還告訴我在比較不一樣的框架時要問哪些問題。例如,我如今會問諸如「每一個框架執行更新的方式如何影響性能和用戶體驗?」等問題,而不是關注GitHub上的Stars。github

另外一個好處是增長你對優秀應用程序架構的好感和理解。雖然大多數開源項目一般與其存儲庫遵循相同的結構,但每一個項目都各有差別。Mithril的結構是很是平坦的,若是你熟悉它的API,你能夠猜想有關文件夾,如代碼renderrouterrequest。另外一方面,React的結構反映了它的新架構。維護者將負責UI更新(react-reconciler)的模塊與負責呈現DOM元素(react-dom)的模塊分開。web

這樣作的好處之一是,如今開發人員能夠經過掛鉤來編寫本身的自定義渲染器react-reconciler。我最近研究過的模塊捆綁包Parcel也有像React packages這樣的文件夾。關鍵的模塊已命名parcel-bundler,它包含負責建立捆綁包,啓動熱模塊服務器和命令行工具的代碼。chrome

不久以後,你正在閱讀的源代碼將引導您進入JavaScript規範。

另外一個好處 —— 令我感到驚訝的是:你能夠更輕鬆地閱讀那些定義JavaScript這門語言如何工做的官方規範。我第一次讀規範是在我調查的區別throw Errorthrow new Error(擾流警報是none)。我調查了這個由於我注意到Mithril用於throw Error實現它的m功能,我想知道使用它是否比throw new Error更好。從那之後,我還了解到,邏輯運算符&&|| 不必定返回布爾值,發現這個 == 運算是如何強制轉換值的規則 ,以及還有Object.prototype.toString.call({}) 返回 '[object Object]'理由

閱讀源代碼的技巧

有不少方法能夠處理源代碼。我發現最簡單的方法是從您選擇的庫中選擇一種方法並記錄調用它時會發生什麼。不要記錄每一步,而要嘗試肯定其總體流程和結構。

我最近按這個方式作了ReactDOM.render的記錄,也所以學到了不少關於React Fiber及其實現背後的一些緣由。值得慶幸的是,因爲React是一個流行的框架,我在同一個問題上遇到了不少其餘開發人員撰寫的文章,這讓我少走不少彎路。

此次深刻探討還向我介紹了合做調度的概念,window.requestIdleCallback方法和連接列表真實示例(React經過將它們放入隊列中來處理更新,隊列是優先級更新的連接列表)。執行此操做時,建議使用庫建立一個很是基本的應用程序。這使得調試時更容易,由於你沒必要處理由其餘庫引發的堆棧跟蹤。

若是我沒有進行深刻審查,我將打開/node_modules我正在處理的項目中的文件夾,或者我將轉到GitHub存儲庫。當我遇到錯誤或有趣的功能時,一般會發生這種狀況。在GitHub上閱讀代碼時,必定要確保你正在閱讀的代碼是最新的版本。你能夠經過單擊用於更改分支的按鈕並選擇「tags」來查看具備最新版本標記的提交中的代碼。由於代碼庫和框架一直都在迭代更新,你固然也不但願瞭解一些可能在下一版本中刪除的內容,因此及時留意最新的版本。

另外一種複雜點的閱讀源代碼的方式,我喜歡稱之爲「粗略一瞥」。在我開始閱讀代碼的早期,我安裝了express.js,打開了它的/node_modules文件夾並安裝了它的依賴。若是看了README文件沒有給到我很好理解的話,我會直接閱讀源碼。這樣作讓我獲得瞭如下這個有趣的發現:

  • Express依賴於兩個模塊,這兩個模塊合併對象但以很是不一樣的方式這樣作。merge-descriptors只添加直接在源對象上直接找到的屬性,它還合併不可枚舉的屬性,同時utils-merge只迭代對象的可枚舉屬性以及在其原型鏈中找到的屬性。merge-descriptors使用Object.getOwnPropertyNames()Object.getOwnPropertyDescriptor()同時utils-merge使用for..in;
  • setprototypeof模塊提供了一種設置實例化對象原型的跨平臺方式;
  • escape-html 是一個78行模塊,用於轉義一串內容,以即可以在HTML內容中進行插值。

雖然這些發現並不能立刻運用,可是讓你對代碼庫或框架使用的依賴關係有一個大體的瞭解。

在調試前端代碼時,瀏覽器的調試工具是你最好的朋友。除此以外,它們可讓你隨時中斷程序運行並檢查當前的狀態,再決定跳過函數的執行或進入或退出程序。若是代碼已經壓縮過,可能沒法很好地進行調試。我我的就喜歡把沒有壓縮的代碼複製到/node_modules文件夾中的相關文件中,再將其進行解析。

案例研究:Redux的Connect鏈接功能

React-Redux是一個用於管理React應用程序狀態的庫。在處理諸如此類的流行庫時,我首先會搜索一些寫過有關其實現的文章。在本案例研究中,我遇到了這篇文章。這是閱讀源代碼的另外一個好處。研究階段一般會引導你閱讀這樣的文章,這些文章會改善你本身的思考和理解。

connect是一個React-Redux函數,它將React組件鏈接到應用程序的Redux存儲。他是怎麼作的呢?根據文檔,它作了如下事情:

「...返回一個新的,鏈接的組件類,它包裝你傳入的組件。」

看完以後,我會問下列問題:

  • 我是否知道函數接受輸入的任何模式或概念,而後返回包含其餘功能的相同輸入?
  • 若是我知道任何這樣的模式,我將如何根據文檔中給出的解釋實現這一點?

一般,下一步是建立一個使用的很是基本的示例應用程序connect。可是,在這種狀況下,我選擇使用咱們在Limejump上構建的新React應用程序,由於我想connect在最終將進入生產環境的應用程序的上下文中理解。

我關注的組件看起來像這樣:

class MarketContainer extends Component {
 // code omitted for brevity
}

const mapDispatchToProps = dispatch => {
 return {
   updateSummary: (summary, start, today) => dispatch(updateSummary(summary, start, today))
 }
}

export default connect(null, mapDispatchToProps)(MarketContainer);

它是一個容器組件,包裹着四個較小的鏈接組件。你在文件中遇到的第一個導出connect方法是註釋:connect是connectAdvanced上的外觀。沒有走得太遠,咱們就有了第一個學習時刻:一個觀察立面設計模式的機會。在文件的末尾,咱們看到connect導出一個名爲的函數的調用createConnect。它的參數是一堆已被解構的默認值,以下所示:

export function createConnect({
 connectHOC = connectAdvanced,
 mapStateToPropsFactories = defaultMapStateToPropsFactories,
 mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
 mergePropsFactories = defaultMergePropsFactories,
 selectorFactory = defaultSelectorFactory
} = {})

一樣,咱們遇到了另外一個學習時刻:導出調用函數解構默認函數參數。解構部分是一個學習時刻,由於代碼編寫以下:

export function createConnect({
 connectHOC = connectAdvanced,
 mapStateToPropsFactories = defaultMapStateToPropsFactories,
 mapDispatchToPropsFactories = defaultMapDispatchToPropsFactories,
 mergePropsFactories = defaultMergePropsFactories,
 selectorFactory = defaultSelectorFactory
})

它會致使發生這個錯誤。Uncaught TypeError: Cannot destructure property 'connectHOC' of 'undefined' or 'null'. 這是由於這個函數沒有默認參數可供使用。

注意有關此內容的更多信息,請閱讀David Walsh的文章。根據你對語言的瞭解,一些學習時刻可能看起來微不足道,所以最好將注意力放在您之前從未見過或須要瞭解更多信息的事情上。

createConnect它自己在函數體中沒有任何做用。它返回一個名爲的函數connect,我在這裏使用的函數:

export default connect(null, mapDispatchToProps)(MarketContainer)

它須要四個參數,都是可選的,前三個參數都經過一個match函數來幫助根據參數是否存在以及它們的值類型來定義它們的行爲。如今,由於提供的第二個參數match是導入的三個函數之一connect,我必須決定要遵循哪一個線程。

若是這些參數是函數,用於包裝第一個參數的代理函數connectisPlainObject用於檢查普通對象的實用程序或warning揭示如何設置調試器以中斷全部異常的模塊,有學習時刻。在匹配函數以後,咱們來了connectHOC一個函數,它接受咱們的React組件並將它鏈接到Redux。它是另外一個返回的函數調用,wrapWithConnect它實際上處理將組件鏈接到存儲的函數。

看看connectHOC它的實現,我能夠理解爲何它須要connect隱藏它的實現細節。它是React-Redux的核心,包含不須要經過暴露的邏輯connect。儘管我本來打算在這個地方結束對它的深度探討,我也會繼續,這也將是我翻查以前發現的參考資料的最佳時機,由於有些資料對對代碼庫的解釋很是詳細。

摘要

閱讀源代碼一開始是很困難的,但跟任何事情同樣,隨着時間的推移它會慢慢變得更容易。咱們的目標不是說要理解每一行代碼,而是要經過閱讀進而獲得不一樣的視角和新的知識。關鍵是既對整個過程進行深思熟慮,也要對全部事情充滿好奇。

例如,我發現isPlainObject函數頗有趣,由於它使用它if (typeof obj !== 'object' || obj === null) return false來確保給定的參數是一個普通的對象。當我第一次閱讀它的實現過程,我想知道爲何它沒有使用Object.prototype.toString.call(opts) !== '[object Object]',這樣會產生更少的代碼並能區分對象和對象子類型,好比Date對象。可是,閱讀下一行意識到,在開發人員幾乎沒法用connect去返回一個Date對象,好比,這個是由Object.getPrototypeOf(obj) === null 進行檢查校驗的。

另外一個很吸引人的是在isPlainObject中的這段代碼:

while (Object.getPrototypeOf(baseProto) !== null) {
 baseProto = Object.getPrototypeOf(baseProto)
}

在谷歌搜索的時候,有些會引導我進入這個 StackOverflow社區 或 Redux issue 查看關於該代碼如何處理的案例,例如檢查iFrame的對象來源。

另外,還有一些有利於閱讀源碼的文章

Finish

【做者簡介】:土撥鼠,蘆葦科技web前端開發工程師,表明做品:飛花亭小程序、續航基因、YY表情紅包、YY疊方塊直播競賽小遊戲。擅長網站建設、公衆號開發、微信小程序開發、小遊戲、公衆號開發,專一於前端框架、服務端渲染、SEO技術、交互設計、圖像繪製、數據分析等研究。

歡迎和咱們一塊兒並肩做戰: web@talkmoney.cn

訪問 www.talkmoney.cn 瞭解更多

相關文章
相關標籤/搜索