[譯]你並不知道Node

你並不知道Node

原文:You don’t know Nodenode

譯者:neal1991git

welcome to star my articles-translator , providing you advanced articles translation. Any suggestion, please issue or contact megithub

LICENSE: MIT瀏覽器

在今年 Forward.js (一個 JavaScript)會議中,我作了主題爲「你並不知道 Node」的演講。在這個演講中,我向觀衆提出一些關於 Node.js 運行時的具備挑戰性的問題,大多數技術相關觀衆沒法回答其中的大部分問題。服務器

我並無進行實際的統計,可是在那個房間感受是這個樣子的。而且有一些勇敢的觀衆在演講以後向我認可了這一事實。數據結構

這也就是我作這次演講的緣由。我不認爲咱們以正確的方式教學 Node!大多數的 Nodejs相關的教學概念集中於Node 包卻不是它的運行時。大多數的包都將 Node 運行時包裹在模塊中(好比 http 或者 stream)。當你遇到問題的時候,這些問題可能存在於運行時之中,若是你不知道Node 運行的話, 你就陷入麻煩了。app

這個問題:多數的 Nodejs相關的教學概念集中於Node 包卻不是它的運行時異步

我爲這篇文章選擇了一些問題而且作出回答。它們將以標題的形式在下面呈現。首先嚐試在你本身心中作出回答!函數

若是你發現錯誤或者具備誤導性的回答,請讓我知道。ui

問題 #1: 什麼是調用棧而且它是V8的一部分麼?

調用棧固然是 V8 的一部分。它是 V8 用來追蹤函數調用的一種數據結構。每次咱們調用一個函數的時候,V8都會將向函數調用棧中壓入一個對於函數的引用,它對於其它函數的內嵌函數也會這樣作。這也包括遞歸調用函數的函數。

clipboard.png

當函數中的內嵌函數到達末端的時候,V8 就會每次彈出一個函數而且在它的位置使用返回值。

爲何理解 Node 是重要的?由於你在每一個 Node 進程中只能獲取一個調用棧。若是你讓這個調用棧保持忙碌,那麼你的整個 Node 進程也會是忙碌的。記住這一點。

問題 #2: 什麼是事件循環? 它是V8的一部分嗎?

你認爲事件循環是在這張圖的什麼地方?

clipboard.png

事件循環是由 libbuv 庫提供,它不是 V8 的一部分。

事件循環處理外部事件而且將它們轉換成回調調用。它是一個從事件隊列中跳去事件的循環而且將它們的回調壓入到調用棧中。它也是一個多相回調。

若是這是你第一次據說事件循環,這些概念將可能不會那麼有用。這個事件循環是一張更大的圖的一部分。

clipboard.png

你須要理解這張更大的圖從而理解事件循環。你須要理解 V8 的角色,知道 Node 的 API,而且知道事件如何進入隊列從而被 V8 所執行。

Node 的 API 就是一些函數,好比 setTimeout 或者 fs.readFile。這些並非 JavaScript 中的一部分。它們是由 Node 提供的函數。

事件循環位於這張圖(真的是一個更復雜的版本)的中間位置,就好像是一個組織者。當 V8 調用棧爲空的時候,事件循環能夠決定下一步執行哪個。

問題 #3: 當調用棧以及事件循環都爲空的時候,Node會作什麼?

很簡單,它會退出。

當你運行一個Node程序的時候,Node 將會自動開始事件循環而且當事件循環變爲 idle 的時候而且沒有其它的事情須要作的時候,這個進程將會退出。

爲了保持 Node進行運行,你須要在事件隊列的某個地方放一些東西。好比,當你啓動一個計時器或者一個 HTTP 服務的時候,你基本上就是告訴事件循環保持運行而且檢查這些事件。

問題 #4: 除了V8以及 Libuv,Node 還有哪些外部依賴?

下面的是 Node 進行須要的全部的單獨庫:

  • http-parser
  • c-ares
  • OpenSSL
  • zlib

它們對於 Node 來講都是外部依賴。它們具備它們本身的源代碼。它們具備它們本身的證書。Node 只是使用它們。

你但願記住由於你想知道你的程序在什麼地方運行。若是你在處理數據壓縮,你可能遇到一些 zlib 庫使用的一些困難。你可能在解決一個 zlib 的 bug。不要把全部的事都怪罪於 Node。

問題 #5: 能夠不使用V8來運行 Node?

這多是一個棘手的問題。你須要一個 VM 來運行 Node 進程,可是 V8 並非你惟一能夠試用的 VM。你可使用 Chakra

獲取 Github倉庫來跟追蹤 node-chakra 的進程:https://github.com/nodejs/nod...

問題 #6: module.exports 和 exports 的區別是什麼?

你能夠老是試用 module.exports 來導出你模塊的 API。你也能夠exports 除了一種狀況:

module.exports.g = ...  // Ok
exports.g = ...         // Ok

module.exports = ...    // Ok
exports = ...           // Not Ok

爲何?

exports 只是一個對於 module.exports 的引用或者別名。當你改變 exports 的時候,你是在改變那個引用而不是改變官方的 API(module.exports)。你將只會得到一個模塊做用域內的局部變量。

問題 #7: 頂級變量怎麼不是全局變量?

若是你有一個 module1 定義了一個頂級變量 g

// module1.js
var g = 42;

而且你有一個 module2 引入了 module1 而且嘗試訪問變量 g,你將會發現 g 是未定義的

爲何?在瀏覽器中你若是作一樣的操做,你可以在全部的變量在定義以後被引入就能夠訪問頂級變量。

每個 Node 文件都會在背後獲取它本身的當即執行函數表達式(IIFE)。全部在這個 Node 文件裏面定義的變量都是在這個 IIFE 做用域內。

相關問題:運行下面僅僅包含一行代碼的 Node 文件的輸出回事什麼:

// script.js
console.log(arguments);

你將會看到一些參數!

clipboard.png

爲何?

由於 Node 執行的是一個函數。 Node 將你的代碼使用函數來包裝而且這個函數準肯定義了上面你看到的5個參數。

問題 #8: 對象exportsrequiremodule都是在每一個文件中全局可用,但每一個文件都不一樣。 這是如何實現的?

當你須要使用 require 對象的時候,你就能夠直接使用它就好像是一個全局變量。然而,若是當你在不一樣的兩個文件中檢測 require,你將會看到兩個不一樣的對象。這是如何實現的?

都是由於相同的神奇的 IIFE:

clipboard.png

正如你所見,神奇的 IIFE 將你的代碼傳遞到5個參數之中:exportsrequiremodue__filename以及__dirname

當你在 Node 試用這5個參數的時候,它們看起來是全局的,可是事實上它們僅僅是函數的參數。

問題 #9: Node 中的循環模塊依賴是什麼?

若是你有一個 module1 引入 module2 而且一樣 module2 也引入了 module1,那麼將會發生什麼?一個錯誤?

// module1
require('./module2');
// module2
require('./module1');

你將不會獲得一個錯誤。Node 容許這種狀況。

那麼在 module1 引入 module2 的時候,可是由於 module2 須要 module1 而且 module1 還沒有完成,module1 將會僅僅獲取 module2的一個部分版本。

你將被警告。

問題 #10: 何時試用文件系統中的同步方法 (好比 readFileSync)是能夠的?

Node 中的每個 fs 方法都有一個同步的版本。爲何你要使用一個同步方法而不是一個異步方法呢?

有時候使用同步方法是不錯的。好比,在初始化步驟中服務器依然在加載的狀況下使用同步方法。大多數狀況是初始化步驟以後的全部事取決與在初始化步驟中獲取的數據。在不引入回調的層級,使用同步方法是能夠接受的,只要你使用同步方法是一次性的事情。

然而,若是你在一個處理程序中試用同步方法,好比 HTTP 服務器請求回調,那將會很明顯 100% 報錯。不要那樣作。


我但願你可以回答一些或者所有這些具備挑戰性的問題。我將會給一些除了 Node.js 基本概念之外的文章。
https://medium.freecodecamp.o...
https://medium.freecodecamp.o...
https://medium.freecodecamp.o...
https://medium.freecodecamp.o...
https://medium.freecodecamp.o...
https://medium.freecodecamp.o...

相關文章
相關標籤/搜索