node學習筆記

liuxixi發表於2017-08-16

1、Node,可以讓javascript執行在伺服器端的平臺。

   是一個為實時Web(Real-time Web)應用開發而誕生的平臺。充分考慮了實時響應,超大規模資料要求下架構。

2、摒棄了傳統平臺依靠多執行緒來實現高併發的設計思路,而採用了單執行緒、非同步式I/O,事件驅動式的程式設計模型。

不僅帶來了巨大的效能提升,還減少了多執行緒程式設計複雜性,進而提高了開發效率。

3、單執行緒事件驅動的非同步I/O。

單執行緒事件驅動的非同步I/O比傳統的多執行緒阻塞式I/O到底好在哪裡:簡而言這,非同步式I/O少了多執行緒的開銷。對操作

系統來說,建立一個執行緒的代價是十分昂貴的,需要給它分配記憶體、列入排程,同時線上程切換的時候還要執行記憶體換頁,

CPU的快取被清空,切換回來的時候還要重新從記憶體中讀取資訊,破壞了資料的區域性性。

4、Node.js 的事件迴圈機制

Node.js 在什麼時候會進入事件迴圈呢?答案是 Node.js 程式由事件迴圈開始,到事件循

環結束,所有的邏輯都是事件的回撥函式,所以 Node.js 始終在事件迴圈中,程式入口就是

事件迴圈第一個事件的回撥函式。

5、模組(Module)和包(Package)是 Node.js 最重要的支柱。

6、事件:Node.js所有的非同步I/O操作在完成時都會傳送一個事件到事件佇列。在開發者看來,事件由EventEmitter物件提供。

前面提到的fs.readFile和http.createServer的回撥函式都是通過EventEmitter來實現的。

7、我們可以把要執行的語句作為node -e的引數直接執行。例如:node -g "cosole.log('Hello World');"

8、使用node的REPL模式。REPL(Read-eval-print loop),即輸入-求值-輸出迴圈。執行無引數node將會啟動一

個JavaScrit的互動式shell:

例如:                      

進入repl模式後,會出現一個">"提示符提示你輸入命令,輸入後按回車,Node.js將會解析並執行命令。如果你執行了一個函式,那麼REPL還會在下面顯示這個函式的返回值。上面的undefined就是console.log的返回值。如果你輸入了一個錯誤的指令,REPL 則會立即顯示錯誤並輸出呼叫棧。在任何時候,連續按兩次 Ctrl + C 即可推出Node.js 的 REPL 模式。

node 提出的 REPL 在應用開發時會給人帶來很大的便利,例如我們可以測試一個包能否正常使用,單獨呼叫應用的某一個模組,執行簡單的計算等。

9、事實上指令碼檔案的副檔名不一定是js,例如我們將指令碼儲存為script.txt,使用node script.txt命令同樣可以執行。副檔名使用.js只是一個約定而已,遵循了JavaScript指令碼一貫的命名習慣。

10、 在開發 Node.js 實現的 HTTP 應用時會發現,無論你修改了程式碼的哪一部份,都必須終止Node.js 再重新執行才會奏效。這是因為 Node.js 只有在第一次引用到某部份時才會去解析指令碼檔案,以後都會直接訪問記憶體,避免重複載入。Node.js的這種設計雖然有利於提高效能,卻不利於開發除錯,因為我們在開發過程中總是希望修改後立即看到效果,而不是每次都要終止程式並重啟。supervisor 可以幫助你實現這個功能,它會監視你對程式碼的改動,並自動重啟 Node.js。

使用方法很簡單,首先使用 npm 安裝 supervisor:

$ npm install -g supervisor

如果你使用的是 Linux 或 Mac,直接鍵入上面的命令很可能會有許可權錯誤。原因是 npm

需要把 supervisor 安裝到系統目錄,需要管理員授權,可以使用 sudo npm install -g

supervisor 命令來安裝。

使用 supervisor 命令啟動 app.js:

$ supervisor app.js

當程式碼被改動時,執行的指令碼會被終止,然後重新啟動。supervisor 這個小工具可以解決開發中的除錯問題。

11、回撥函式

讓我們看看在 Node.js 中如何用非同步的方式讀取一個檔案,下面是一個例子:

//readfile.js

var fs = require('fs');

fs.readFile('file.txt', 'utf-8', function(err, data) {

if (err) {

console.error(err);

} else {

console.log(data);

}

});

console.log('end.');

執行的結果如下:

end.

Contents of the file.

要想理解結果,我們必須先知道在 Node.js 中,非同步式 I/O 是通過回撥函式來實現的。fs.readFile 接收了三個引數,第一個是檔名,第二個是編碼方式,第三個是一個函式,我們稱這個函式為回撥函式。JavaScript 支援匿名的函式定義方式, 譬如我們例子中回撥函式的定義就是巢狀在fs.readFile 的參數列中的。這種定義方式在 JavaScript 程式中極為普遍,與下面這種定義方式實現的功能是一致的:

//readfilecallback.js

function readFileCallBack(err, data) {

if (err) {

console.error(err);

} else {

console.log(data);

}

}

var fs = require('fs');

fs.readFile('file.txt', 'utf-8', readFileCallBack);

console.log('end.');

fs.readFile 呼叫時所做的工作只是將非同步式 I/O 請求傳送給了作業系統,然後立即

返回並執行後面的語句,執行完以後進入事件迴圈監聽事件。當 fs 接收到 I/O 請求完成的

事件時,事件迴圈會主動呼叫回撥函式以完成後續工作。因此我們會先看到 end.,再看到

file.txt 檔案的內容。

12、模組和包

模組(Module)和包(Package)是Node.js最重要的支柱。

Node.js提供了require函式來呼叫其他模組,而且模組都是基於檔案的,機制十分簡單。

我們經常把Node.js的模組和包相提並論,因為模組和包是沒有本質區別的,兩個概念也時常混淆。如果要辨析,那麼可以把包理解成是實現了某個功能模組的集合,用於釋出和維護。

13、模組是Node.js應用程式的基本組成部分,檔案和模組是一一對應的。換言之,一個Node.js檔案就是一個模組,這個檔案可能是JavaScript程式碼、JSON或者編譯過的C/C++擴充套件。

例如:var http=require(‘http’),其中http就是Node.js的一個核心模組,其內部是用C++實現的,外部用javaScript封裝。我們通過require函式獲取這個模組,然後才能使用其中的物件。

14、Node.js提供了exports和require兩個物件,其中exports是模組公開的介面,require用於從外部獲取一個模組的介面,即所獲取模組的exports物件。

讓我們以一個例子來了解模組。建立一個 module.js 的檔案,內容是:

//module.js

var name;

exports.setName = function(thyName) {

name = thyName;

};

exports.sayHello = function() {

console.log('Hello ' + name);

};

在同一目錄下建立 getmodule.js,內容是:

//getmodule.js

var myModule = require('./module');

myModule.setName('BYVoid');

myModule.sayHello();

執行node getmodule.js,結果是:

Hello BYVoid

在以上示例中,module.js 通過 exports 物件把 setName 和 sayHello 作為模組的訪

問介面,在 getmodule.js 中通過 require('./module') 載入這個模組,然後就可以直接訪

問 module.js 中 exports 物件的成員函式了。

這種介面封裝方式比許多語言要簡潔得多,同時也不失優雅,未引入違反語義的特性,

符合傳統的程式設計邏輯。在這個基礎上,我們可以構建大型的應用程式,npm 提供的上萬個模

塊都是通過這種簡單的方式搭建起來的。

15、單次載入

上面這個例子有點類似於建立一個物件,但實際上又和物件有本質區別,因為require不會重複載入模組,也就是說無論呼叫多少次require,獲取的模組都是同一個。

看下列程式碼:

//loadmodule.js

var hello1 = require('./module');

hello1.setName('BYVoid');

var hello2 = require('./module');

hello2.setName('BYVoid 2');

hello1.sayHello();

執行後發現結果是Hello BYVoid 2,這是因為變數hello1,hello2指向的是同一物件,因為hello1.setName的結果被hello2.setName覆蓋,最終輸入結果是由後者決定的。

 

相關文章