當我們學習 Node.js 時,我們在學習什麼?

Node.js JavaScript 編程語言 SQL GitChat技術雜談 GitChat技術雜談 2017-08-25

本文來自作者:死月GitChat 上精彩分享。

在寫文章的最開頭,我想引用一段我已經引用爛了的一段前同事的話,也是對於 Node.js 圈中近幾年開始浮躁的風氣的一種質疑。

大家都說學 Node.js 學 Node.js,到底是學它的什麼呢?是學 JavaScript 這門語言,還是學 Node.js 的 API?還是學 Node.js 各種三方庫?

正如問題所說,Node.js 其實就三塊內容,一塊是 JavaScript,也就是老生常談的 ECMAScript;

另一部分是也就是它的 API;最後一部分也就是最不需要學的,也就是生態圈中的各種庫。

表象之學

學 Node.js 的人大抵是兩個方向過來的,一是前端同學,二是後端同學。

對於前端同學來學 Node.js 來說,對於他們最大的優勢是 Node.js 使用了 ECMAScript,與他們的老本行一樣一樣的;

而對於後端同學來說,優勢在於他們有後端的一整套體系思想,而對於 Node.js 的 API 看起來會更加好理解。

ECMAScript

ECMAScript 是 Node.js 的根基,也就是大家所熟知的 JavaScript。目前市面上使用地最多的其實還是 ES5,即使是前端同學在寫 ES6,在經打包之後,通常編譯成的還是 ES5 的代碼——出於兼容考慮。

而對於 Node.js 來說,基本上的 ES6 語法都已支持,但是還遺留了一些小坑,如 let 的效率實際上沒有 var 高等。

舉個例子,在 Node.js 的一些源碼中,很多的提交都是將 var 批量替換成 const,而少見批量將 var 替換成 let,比如這裡。不過由於新版的 V8 中開啟了 TurboFan,這效率就不一定了。

不過,哪怕是前端小夥伴們,也不一定能把 ECMAScript 給啃全咯。除了書中自有黃金屋之外,我在這裡更推薦的是學習如何看 ECMAScript 規範

ECMAScript 要入門,很簡單,無非是 C-Style。稍微精髓點的就是原型與原型鏈那一套了。

不過現在 ES6 出來,大家習慣了 class 之後,興許就把原型鏈給淡忘了。希望大家還是不忘本,這東西還是得拾起來。

但是一到比較偏僻的地方,就需要網上提問了。比如這些問題:

  1. 為什麼 1 + undefined 結果是 NaN,而 1 + null 結果卻是 1?( https://www.zhihu.com/question/56007530/answer/147330933 )

  2. 關於兩個 {} 的比較(如 ==!=<=>= 等)結果的真假值問題。( https://www.zhihu.com/question/56013048/answer/147328626 )

就結果來看,很多已經理解的人就不必吱聲了,大多其他小夥伴要麼是靠死記結果,要麼就是上百度、知乎等地兒搜,搜出來還大多是你抄我我抄你的答案,剩下的小縷同學就乾脆不知道結果了。

其實去 StackOverflow 等地查效果應該會更好,然而答案最直接的地方還數 ECMAScript 規範(https://www.ecma-international.org/)了。

其實在一看到這兩個問題的時候,我也是不知道原因的,也信不過網上的大多答案解釋。然後就上 ECMAScript 規範去追根溯源了。

首先第一個問題,它就是關於操作符 + 的一些定義,我們稍微檢索一下,就能找到它的出處在 ES5 的(再早的版本我們就不追溯了)11.6.1 節(https://www.ecma-international.org/ecma-262/5.1/#sec-11.6.1)中。

這一節的內容講述的就是加號操作符的計算方法。將兩個值先執行一個 ToPrimitive 操作,而對於這個操作,ECMAScript 規範中也原原本本給出了一張對照表。

當我們學習 Node.js 時,我們在學習什麼?

1undefinednull 三個值經過 ToPrimitive 操作後一點都沒變;接下去判斷左右值是否有字符串,若沒有,則將兩個值再進行 ToNumber 操作進行相加。然後對於 ToNumber 操作,規範也原原本本給出了一張對照表。

當我們學習 Node.js 時,我們在學習什麼?

從這張對照表看出來,undefined 的結果是 NaNnull 的結果是 +0,自然兩個相加結果一個是 NaN 一個是 1 了。

第二個問題的結果也是類似,只要順藤摸瓜,自然能在規範中找到相應的答案。其實什麼原因不原因的,最後追溯到結果都是人(也就是委員會)制定出來的結果。

這裡略微提醒一下,等值操作和大小比較操作分別在 ES5 的 11.9.3 節(https://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3)和 11.8.5 節(https://www.ecma-international.org/ecma-262/5.1/#sec-11.8.5)中。

所以本節中,我們說的就是要學好 JavaScript,在初步入門之後,藉助 ECMAScript 規範加深自己對其的理解

Node.js API

講完 ECMAScript,我們接下來再要講講的就是 Node.js 內置的 API 了。大家都知道,Node.js 就是靠著 V8 來作為 JavaScript 的運行引擎,libuv 作為事件循環的框架,然後上面像膠水一樣黏上一堆易用的 API 就成了。

所以這些 API 就是大家入門 Node.js 的時候最先遇到的必修課了。

相信很多人跟我一樣,第一次接觸 Node.js 的時候都是這樣的幾行代碼:

當我們學習 Node.js 時,我們在學習什麼?

一開始學的人可能會覺得很神奇,其實現在看來無非就是 Node.js 內置的 http 模塊中的一些 API,如 http.createServer(callback) 等。

要說這些 API 去哪學,其實用不著看什麼網上的教程啊什麼東西,所有的這些原原本本都在 Node.js 的文檔之內。

如 6.x 的 Node.js 文檔就可以看(https://nodejs.org/dist/latest-v6.x/docs/api/),裡面無非三十幾個模塊,而且內容都十分簡單且實用。

例如 http 和 https 模塊就是我們最常用的用於創建我們 HTTP(S) 服務器以及用於 HTTP(S) 客戶端請求的模塊,雖然現在大家用於線上開發大多都用了類似 Express、Koa 等框架或者 Request 這類庫,但是實際上他們的最底層用的還是 Node.js 的這些模塊這些 API。

API 沒多少要學的,就算真的從頭到尾看一遍,甚至也花不了一天(前提是對其中的各種前置知識瞭解)。大不了在一開始學的時候先過一遍,揀常見的學熟絡了,然後在日常開發中再積攢肌肉記憶,就差不多了。

為什麼這麼說呢,因為哪怕是你真的死記硬背背下來了,下個版本說不定得變。舉個例子來說,dns.setServer() 這個函數,自 Node.js 8.2.0 起,就支持了自定義 DNS 服務器端口,如:

dns.setServers([ "103.238.225.181:666" ]);

這個函數的新特性自 Issue#7903 (https://github.com/nodejs/node/issues/7903)提出並在 PR#13723 (https://github.com/nodejs/node/pull/13723)中被實現並且在 8.2.0 版本中正式發佈。

除此之外,在 Node.js 的棄置 API 中也列了一堆在各種版本中被棄用的 API,就我以前用最多的就是 fs.exists() 函數,但是在 1.0.0 中就被標記為棄用了,只是目前來說還沒有下架而已——指不定哪天就下架了呢。

這些被棄用的 API 也在 Node.js 的文檔(https://nodejs.org/dist/latest-v8.x/docs/api/deprecations.html)中被列了出來。

總之在 Node.js 的快速迭代中,各種事情都是有可能的。在維護者和貢獻者的提交中,分為 Patch 提交、SEMVER-MINOR 和 SEMVER-MAJOR 三類提交,其中 SEMVER-MINOR 是中間的版本號更新,即非 Break 但是有功能改動、更新的提交。

而 SEMVER-MAJOR 指的是那類 SEMVER-MAJOR 提交,如下架 API 就是一類 SEMVER-MAJOR 的提交。

由此可見,學 Node.js 的 API 時,我們大多隻需要記住並熟練使用常用的一些即可,其餘的就仰仗實時翻閱 Node.js 文檔,以及關注 Node.js 新版本發佈時候的 Changelog,尤其是那些 Notable changes(https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V8.md#notable-changes-1)。

再下一步就可以有事沒事去看看 Node.js 源碼倉庫中的 PR 了,參與討論討論也是不錯的選擇,還能參與到 Node.js 的貢獻當中來。

Node.js 生態圈

個人認為,這是 Node.js 中最不需要學的一塊內容了。

怎麼說呢,Node.js 的生態圈非常龐大,其生態圈中的庫質量參差不齊。有有著很大用戶群的 Express、Koa 等,也有著鬧著玩的 five 這類庫。

無論是什麼,總之根源還是在於檢索加上看文檔以及造輪子

首先第一步檢索,無論是谷歌、NPM 自帶的搜索引擎,還是自己腦海裡的人肉搜索庫,都是檢索的根基所在。

例如我們如果要寫一個 Web 應用,首先就是想到要一個 Web 框架,然後腦子裡應該首先冒出 Express、Koa 等既是框架又算不上框架的 Web 庫,或者再小眾點就是 hapi、egg 等框架;

如果覺得自己想另闢蹊徑,找找看有沒有什麼更小眾能體現自己逼格的東西,就可以上搜索引擎搜了,比如 Node.js Web Framework 等關鍵字;

最後,如果覺得還不過癮,那就自己造哇,比如我們團隊就用的是自己造的 Akyuu.js 框架(https://github.com/akyuujs/akyuu 框架還在沉澱中,文檔還未來得及寫)。

再比如,我們在寫 Web 應用的時候,要用 md5 給密碼進行簡易加密。其中一個辦法是使用 Node.js 的 crypto 模塊,但是寫起來也略冗長,還有個辦法就是去 NPM 上搜搜看有什麼好的包可以用咯。

輸入 md5 關鍵字,我們能看到好多可以用的包。可以揀一兩個看著順眼的用,如:

  • md5

  • blueimp-md5

  • md5.js

  • apache-md5

至於用嘛,到自己揀好的包裡面看看文檔就好用了。所以這就是我所說的,Node.js 生態圈中的包,看文檔就夠了。

大不了看官方出的教程,例如 Express 的官網(http://expressjs.com/)。看這些東西要遠好於自己去百度搜索抄來抄去的教程。

還有一類庫,是通過其它的庫被包裝而成的,那在看文檔之前,可能要先去了解一下原庫的一些內容。

舉個例子來說,gm 這個包是 Node.js 中用於圖像處理的包,其原理是通過執行 GraphicsMagick 命令的形式加上管道來進行處理的。

所以要用 gm 的話,還需要事先去了解 GraphicsMagick 的一些用法,尤其是一些參數等等。

還有就是像 tensorflow2 這種包,相信大家都想到了,用它之前還得先去了解 TensorFlow。

最後就是,當大家找不到想要的包的時候,就輪到自己造輪子,反哺 Node.js 生態圈了。

比如我就曾經實現了一套 Node.js 中使用的 ORM 庫,喚作 Toshihiko,與市面上常見的 ORM 如 Sequelize 等大相徑庭,主張簡約和性能,並目前在我廠廣泛應用。

其它更多的我參與或者我發起的一些 Node.js 生態圈的貢獻,可以查看我的 me 倉庫。

所以本節中我們所說的,學習 Node.js 生態圈,無非就是學習如何檢索生態圈的內容、好好過濾並去其糟普取其精華,然後還是很重要一點就是學習如何查看文檔,在有前置知識的庫面前要先學習它的前置知識

包與模塊機制

Node.js 中的包與模塊機制都是借鑑了 CommonJS 的模塊規範和包規範。關於這一塊我曾經開了一場知乎 Live 詳細講解——足足講了三四個小時還是虎頭蛇尾。

這麼長的一塊內容還是值得一學的。如果大家偷個懶,也可以看看我的知乎 Live——《深入瞭解 Node.js 包與模塊機制》(https://www.zhihu.com/lives/842742839304667136)。

異步之旅

很多從後端轉過來學 Node.js 的同學中,一開始入門最大的障礙就是它的異步非常令人煩惱,尤其是回調地獄。

就連我當年一開始學習 Node.js 的時候,也非常不適應。在什麼也不懂的時候硬生生搞了個自己習慣的框架,把裡面的異步操作通過 fibers 硬生生轉成了同步的寫法。現在回過頭來想想,還是太年輕了。

習慣 Node.js 中的異步,個人認為是非常有必要的。

Callback

回調函數是 Node.js 中的基礎所在,大量的內置 API 都是通過回調函數來觸發事件返回結果的,如 fs、http 中的一堆堆函數,以及目前市面上大部分的三方庫。

不過這貨最讓大家煩惱的就是傳說中的回調地獄了。

但是我個人認為這都不是事兒,通過 async 這個包就能比較大程度地化解了,而且它還有非常多地流程控制的函數,形如 eachLimitwaterfallparallelauto 等等,在 async 中就能非常簡單地實現了一堆的流程。

可以說 Callback 正是 Node.js 已至 JavaScript 的精髓所在,連響馬大大都在微博中挺 Callback 了。

callback 雖然慢,但是並不太慢。而為了解決 callback hell 引入的流程框架,一個更比一個慢。導致使用這些框架的應用,性能直線下滑 50-100 倍。【沒錯,說的就是 async】 (指的是 async 語法)

把 callback 玩到這麼精緻,也就是 JavaScript 了。

以及在交流過程中說的話:

如果沒得選(指 fibjs),肯定是用 callback。

這玩意兒,主要就是要熟練,和思維轉換。當大家熟悉了 Node.js 中事件循環的原理後,再回過頭來看 Callback,其實也沒那麼難了。

當再配合上 async 這個包,照樣能寫出非常優雅的代碼。

Promise 與 yield

Promise 是另一套異步之旅中的概念。相信當下入門的 Noder 們應該會比較瞭解 Promise。而且當下的 Node.js 中已經加入了內置的 Promise 支持。

而且實際上在學習 Node.js 中,Promise 相較於 Callback 來說是更易學、易於掌控的,這也是為什麼 Promise 更被大眾所接受。

而 yield 來說,這個概念對於很多其它語言轉過來的後端同學不會陌生,如 python 就有這個概念。它主要配合菊花函數(即生成器函數)使用

而且 Koa 框架中的異步大多使用 yield 來完成。

實際上這一塊來說,仍然是前面章節講到的 ECMAScript 的一塊子內容,所以還是歸於要好好學習 ECMAScript。

async 與 await

這是 Node.js 8 中推出的另一套異步解決方案。在先前的 7 版本中,其實已經加入了該方案的支持,只不過沒加入默認支持而已。

只不過目前而言,實際上 async 與 await 仍然在 ECMAScript 的草案(https://tc39.github.io/ecmascript-asyncawait/)之中,Node.js 中提前把它弄起來了。

如果是 C# 轉過來開發的童鞋應該對該函數不陌生。話說回來,實際上這一塊也算是 ECMAScript 學習的子內容。

深入學習

當大家學會了 Node.js 中《表象之學》一小章的內容之後,可以說是入門了 Node.js 了,這個時候基本上對於平常的 Node.js 開發已經遊刃有餘了。甚至可以就著網上的教程,開始各種 Web 應用的開發了。

不過本文不是教大家如何看教程的,而是別的內容。

表象之學的內容通常有點編程基礎的人來說都易學,前端同學在語言上會更佔便宜一些。如果前端的同學們想開始企業級的線上應用,下面的內容才是應該深入學習的。

雖然看起來下面的內容與 Node.js 無甚關係,但是作為一個合格的開發來說,更深入的一個層次,就是網上流傳甚廣的一句話了。

忘記語言。(雖然 PHP 是最好的語言 ヘ|・∀・|ノ*~●)

數據庫

如果僅把 Node.js 當做玩具來看待,隨便三下五除二學一下數據庫就可以了。

我曾經接手過一個 Node.js 的外包項目二期的開發,非常痛苦。數據庫完全是亂設計,看起來更像是被各種劣質教程害了的那種。

所以我在這裡比較真誠地給大家推薦一下,如果作為 Node.js 的後端開發來說,數據庫是必備的前置知識。

對於學習新興的 NoSQL 來說,還是推薦順帶扎扎 SQL 的相關底子,如 MySQL。

記得大學的時候我們用的數據庫書是自己本校老師寫的,裡面各種原理還是受益匪淺的,雖然說現在很多地方都忘得差不多了,但是學過之後腦子裡有個印象,下次提起的時候就又會恍然大悟了。

例如 SQL 的檢索語句的語法樹、索引相關內容等等。

以及數據量到了一定程度的時候,分表、分庫、讀寫分離等等內容。雖然很多都是 DBA 給的建議,但是也有很多公司是並不具 DBA 崗位的,很多時候都是要靠自己。

不過在線上各種應用當中,大多的 Node.js 程序都是使用了 ORM 來搞定,所以很多時候用不著大家親自寫 SQL 語句,而是直接使用 ORM 裡面的各種函數。

本節說的數據庫學習中,更多還是需要學習如何更好地設計數據庫,弄更好的各種解決方案等等。

在大家紮好了 SQL 的底子之後,也可以開始嘗試著各種 NoSQL 了。目前比較火的就是 MongoDB,並且某種意義上,Redis 這類也是屬於 NoSQL。

後端思想以及代碼抽象

要學習 Node.js,若想做後端開發的話,後端思想以及代碼抽象是非常重要的。

關於後端思想,其實是個很虛很懸的東西。它的整個學習體系中包括但不侷限於這些內容:

  • 安全

  • 網絡編程

  • 算法與數據結構

  • 服務化

  • 數據庫

其中數據庫在前面一節中我們就提到了,是一個非常重要的後端組成部分。安全、網絡編程和算法與數據結構其實在前端中也可以有體現,但是佔比還是比較少,甚至很多前端同學也還沒思考過這些問題。

而對於要學習 Node.js 的童鞋來說,還是推薦學習這些關於後端的思維。

做接口、方案、編碼的時候,首先要考慮到設計是否安全,其次是性能以及服務器壓力等。後端是一個應用最後的防線,一定要嚴把關。

在前端的安全性做得再好,一旦有人繞過前端直接給後端發請求,攻擊就直勾勾發到後端去了。

大多的後端轉 Node.js 的同學在這一方面比前端同學佔便宜,因為思維上大多會慣性地考慮這些點,而前端同學轉過來的時候往往會栽在這些地方,設計接口、方案的時候不夠「後端化」。

所以在學習 Node.js 的時候,學習後端的思維是非常重要的。

關於後端思維,還有很重要的一點就是代碼層面的抽象。舉個簡單的例子,當前很多前端的框架都是抽象成 MVVM,而後端當下最通用的抽象就是 MVC。

我在前面講過一句話,說 Express 等既是框架又不是框架,就是因為它們沒有一個非常好的層次抽象,一千個人寫出來的應用有一千個模樣。

這導致了還是我說的之前接手的一個項目,代碼根本沒有抽象,所有的 SQL 代碼都是硬生生寫到各種路由邏輯當中。

在一個企業的項目當中,這種抽象是非常必要的,一段解耦的代碼會非常好維護,使得接盤俠會特別爽。

其實不只是 MVC 的抽象,抽象還分很多種方式。如很多面向過程的代碼,在稍加修飾之後,就能抽象成優雅面向對象的代碼(當然,在 FP 中另當別論,不過本文講的是跟 Node.js 相關的學習,所以我還是比較推崇面向對象的抽象方法)。

關於抽象還有一個不得不提的就是設計模式,這也是一個合格的程序員(Node.js 程序員當然也是程序員中的一類)可能所需要了解的內容。

尤其是其中的適配器模式、工廠模式、單例模式等,在日常中大家也經常會用到。

那麼,為什麼說算法和數據結構重要呢?可能很多時候大家在平時寫代碼的時候覺得算法或者數據結構離自己都挺遠的。實際上你可能在不知不覺中就用到它了。

Redis 中大家用到的 Set 就是一種常見的數據結構,只不過 Redis 都幫大家給包裹好了,包括查詢的操作,最終通過網絡給返回結果給大家。

再比如,我在我以前寫過一篇文章《我為什麼要使用哈希》(https://xcoder.in/2015/10/16/why-i-use-hash/)中就介紹了哈希的一些妙用。其中文章中的「報告圖問題」和「唯一主鍵問題」兩個問題都是我在實際 Node.js 工作中進行的使用哈希做的一些事情。

這跟後端的思想也都緊密聯繫在了一起,例如為了節省空間的去冗餘,同時由於減少了記錄數也提高了查詢的效率;又例如由於有時候考慮到不使用自增主鍵的情況,就可能要通過哈希來生成一個。

這一塊應該是 Node.js 乃至所有程序員需要學習的最重要的一塊內容了。內容非常多,也不是三言兩語能講完的,就我自己而言也還只是在山腳下往上看,只是希望能給大家指一條路,與諸君共勉。

其它領域

很多時候,當我們做項目在技術選型,除了說生態圈中哪個技術更成熟以外,性能優劣以外,還有一個考慮的重點是,實施項目開發者的趁手兵器是什麼。

論 Web 開發,Java 無疑是更成熟的方案,但是我就是不會 Java,我就是會 Node.js,那如果項目讓我來做,我肯定會選 Node.js——除非有方面遇到了瓶頸。

那麼其它領域也一樣,作為一個能在本地跑的運行時(而不是像前端 JavaScript 一樣只能寄宿在瀏覽器當中),理論上 Node.js 可以做其它同類語言都能做的事。如科學計算、人工智能等等,無非是領域的生態圈強不強。

比如還是我們先前提到的 tensorflow2 這個包,就讓 Node.js 在深度學習的領域中有了可能性。而對於硬件開發,同樣的,我們能用樹莓派中裝上一個 Node.js 來驅動我們用該派組裝的一部小車。

在前端領域中,實際上 Node.js 做的事情非常有限,畢竟它不能在瀏覽器中跑——它主要做的事情是為前端各種生態圈提供命令行工具,以及可以使用類似 nw.js 之類的東西來寫桌面 GUI 應用。

說前端生態圈的命令行工具,實際上用什麼語言寫也都一樣,只不過還是我的那句話,前端生態的開發者對 JavaScript 更熟悉,用 Node.js 更為合適,要知道,在很早之前 JavaScript 代碼壓縮等活也都是通過其它語言寫的腳本或者工具實現的,如 YUICompressor。

所以對於 Node.js 在其它領域中的應用,我的推薦是大家先去了解該領域的各種基礎知識,語言只是工具,換了誰都一樣,包括後端。

最終大家用的時候看的主要還是熟練度,也就是開發成本和開發效率——畢竟除非是真的需要壓價性能到極致的時候,通常性能不會成為我們選型的瓶頸。

就比如我們在做網關開發的時候(這裡有我上次分享的一個 Slide),反向代理層之間上了非常成熟的 OpenResty,而與之交互的請求解析服務,我們毫不猶豫地選擇了我們最熟悉的 Node.js,如果這個項目交給我們公司的 Java 團隊來做,我相信他們也會毫不猶豫地選擇 Java。

源碼剖析

前面講了各種 Node.js 學習時,我們所需要關注的點。後端思想這一塊由於太大,可以貫穿大家的整個程序員生涯(哪怕是隻想拿 Node.js 來寫寫命令行工具,或者 GUI 應用,學習這一塊內容也是無害的)。

剩下的其它點應該是不難的,等大家熟悉了這些點、學習得差不多之後,就可以開始剖析 Node.js 的源碼了。

網上不乏 Node.js 源碼的剖析文章,我這裡也介紹一些比較好的剖析方法。由於文章篇幅原因,我這裡只介紹方法,不做詳細剖析。

首先 V8 是 Node.js 的根本,所以希望大家在剖析之前,能瞭解 V8 的一些基本內容,使大家在閱讀 Node.js 的代碼時不至於太累。

這裡有一篇《Embedder’s Guide》(https://github.com/v8/v8/wiki/Embedder's-Guide),是關於 V8 的基礎,如句柄、句柄作用域等基礎概念。

其次是 libuv,研讀 libuv 文檔會讓我們在理解 Node.js 中一些異步代碼會更容易一些。

不過這一切都需要我們有一定的 C 以及 C++ 的基礎,相信這對我們好奇的程序員們並沒有什麼特別大的難度。

在有了這兩塊的基礎後,我們有兩個方向的閱讀方法。

其一,從入口文件開始看,開始理解各種源碼是如何整合進入 Node.js 的。C++ 的入口文件就是 main 函數的那個文件,從該文件中追根溯源進去,一步一步剝洋蔥一樣看,在遇到比較難以理解的地方的時候不一定要完全搞懂,挑關鍵字和語義來閱讀即可——畢竟 Node.js 的函數很大程度上都是語義化的;

JavaScript 的入口文件則是 lib/internal/bootstrap_node.js 文件,這一點其實在剝洋蔥閱讀 C++ 源碼的時候就會發現,在 Node.js 初始化一定階段的時候會執行這個文件,完成 JavaScript 環境的初始化。

其二,從膠水內置庫開始看。當我們遇到一些問題,如 http 某個函數的實現原理的時候,我們就應該直接跑到 lib/http.js 開始閱讀,在回溯其依賴的各種函數的時候,按照自己的需求去探索函數棧的深度。

在遇到 process.binding() 函數的時候,要根據其獲取的內容,對應到 C++ 代碼中的相應文件,如 process.binding('util') 對應的就是:

https://github.com/nodejs/node/blob/v8.2.0/src/node_util.cc#L236 這個文件。

掌握了這兩種閱讀方式,即使不是太瞭解 V8 的 API 和各種概念以及 libuv 的各種概念,也能比較輕鬆地閱讀 Node.js 的源碼了。

但是如果需要深入閱讀理解 Node.js 源碼的話,還是推薦大家去了解一下關於 V8 和 libuv 的一些概念。

在閱讀代碼的時候,大家也可以動手嘗試修改一下相應的代碼,以驗證自己的理解是否正確。

這個時候就可以把 Node.js 源碼倉庫克隆到自己本地,然後可以著手修改一些內容並在編譯之後執行一下,看看自己修改的地方是否如自己預期所示。

克隆編譯 Node.js 的步驟還是比較簡單的,如果是 *nix 下的用戶,可以這麼搞:

當我們學習 Node.js 時,我們在學習什麼?

其中 <核數> 是你的 CPU 核數,即一個數字,如 make -j8

如果你有其它的疑問或者你是 Windows 下的開發者的話,可以自行閱讀 Node.js 的構建文檔(https://github.com/nodejs/node/blob/master/BUILDING.md)以獲取更多信息。

小結

本文主要給大家介紹了當我們在學習 Node.js 的時候,推薦的正確學習姿勢,防止大家誤入歧途,光學習了最簡單的表象內容,而忽略了學習一門後端語言更深一層次的體系。

其實在「深入學習」一節中,已經脫離了語言的範疇,而且事實上在實際工作中,語言反而是次要的內容,在開發經驗中學習、總結、抽象出一套體系才是核心的思想。

最後再理一遍本文推薦大家在學習 Node.js 的時候比較舒適的一條路線吧。

首先走平地入門的時候,ECMAScript 是根基,在停留在表象語法的階段上,可以更深一層次地去扒一些規範中約定的內容,如各種操作符在內部的流程是怎麼樣的;

在根基紮實的情況下入門 Node.js 是最好的,然後開始閱讀其 API 文檔,內容較淺,主要是快速入門如何使用它的基本 API 以供可以快速開發一些東西;

在熟悉 Node.js 內置 API 的同時,要瞭解它的生態圈,檢索和閱讀文檔的能力是非常必要的,在生態圈無法滿足自身的情況下,一種進階的做法就是自己提供輪子。

在走這一條線路的時候,其它語言轉過來的開發者可能會踩到一些異步的坑。

哪怕平時不使用 Callback 而是去使用 Promise 等東西,貫徹 Callback 的思想還是比較有必要的——不要忘本,畢竟這才是 Node.js 的精髓所在,而其它內容都是別的語言中借鑑過來的,很多別的語言轉過來的開發者會感到比較親切。

在入門之後,大家需要關注的點要從 Node.js 語言本身移開,關注後端開發的一整套體系。當然也不是憑空關注的,而是結合 Node.js 自身的特性,摸索一條適合它的體系。

正如「中國特色社會主義理論體系」一般,再怎麼中國特色它也是社會主義,大家需要學習的是弄出一套「Node.js 特色後端理論體系」。

但是其根本不變,Node.js 特色再怎麼濃厚它還是後端體系,如結合 Node.js 開發的方式,搞出 Express 這類基於中間件的 Web 框架,但是安全、性能、結構分層還是非常有必要的。

還有一個重點就是數據庫需要好好學,而不是簡簡單單地 CURD 會用即可——不然很多公司要 DBA 幹嘛?哪怕我們不去搶 DBA 的飯碗,我們也不能對性能優化、表的設計一無所知隨手一搞。

而脫開後端領域來說,實際上只要你樂意,Node.js 也可以做任何領域的事情,只不過是是否真的適合和是否趁手而已,擺正心態很重要。

學習用 Node.js 快速出原型也是一個不錯的選擇,例如深度學習的領域。

萬丈高樓平地起,在我們深入學習一整套體系的時候,可以根據自身的能力,開始嘗試閱讀 Node.js 源碼,理解它究竟是個怎麼樣的東西——其實它就是一個膠水層,V8 和 libuv 膠上一堆易用的 API。

最後,太極生兩儀,兩儀生四象,四象生八卦,八卦生萬物。Node.js 就是萬物中的一個,而整個編程界可以比作太極,我們從萬物開始學,最後都需要歸宗到太極當中去——當我們在學習 Node.js,我們其實就是在學編程,不要把自己侷限住了

當大家閱讀完這篇文章後,對我的疑問不是侷限於 Node.js 語法如何,異步的時候到底採用什麼方案比較好的時候,我想我的文章思想才算是真正傳遞給各位開發者了。

當我們學習 Node.js 時,我們在學習什麼?

相關推薦

推薦中...