一、Node,可讓javascript運行在服務器端的平臺。javascript
是一個爲實時Web(Real-time Web)應用開發而誕生的平臺。充分考慮了實時響應,超大規模數據要求下架構。java
二、摒棄了傳統平臺依靠多線程來實現高併發的設計思路,而採用了單線程、異步式I/O,事件驅動式的程序設計模型。node
不只帶來了巨大的性能提高,還減小了多線程程序設計複雜性,進而提升了開發效率。shell
三、單線程事件驅動的異步I/O。npm
單線程事件驅動的異步I/O比傳統的多線程阻塞式I/O到底好在哪裏:簡而言這,異步式I/O少了多線程的開銷。對操做編程
系統來講,建立一個線程的代價是十分昂貴的,須要給它分配內存、列入調度,同時在線程切換的時候還要執行內存換頁,緩存
CPU的緩存被清空,切換回來的時候還要從新從內存中讀取信息,破壞了數據的局部性。服務器
四、Node.js 的事件循環機制多線程
Node.js 在何時會進入事件循環呢?答案是 Node.js 程序由事件循環開始,到事件循架構
環結束,全部的邏輯都是事件的回調函數,因此 Node.js 始終在事件循環中,程序入口就是
事件循環第一個事件的回調函數。
五、模塊(Module)和包(Package)是 Node.js 最重要的支柱。
六、事件:Node.js全部的異步I/O操做在完成時都會發送一個事件到事件隊列。在開發者看來,事件由EventEmitter對象提供。
前面提到的fs.readFile和http.createServer的回調函數都是經過EventEmitter來實現的。
七、咱們能夠把要執行的語句做爲node -e的參數直接執行。例如:node -g "cosole.log('Hello World');"
八、使用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 在應用開發時會給人帶來很大的便利,例如咱們能夠測試一個包可否正常使用,單獨調用應用的某一個模塊,執行簡單的計算等。
九、事實上腳本文件的擴展名不必定是js,例如咱們將腳本保存爲script.txt,使用node script.txt命令一樣能夠運行。擴展名使用.js只是一個約定而已,遵循了JavaScript腳本一向的命名習慣。
十、 在開發 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 這個小工具能夠解決開發中的調試問題。
十一、回調函數
讓咱們看看在 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 文件的內容。
十二、模塊和包
模塊(Module)和包(Package)是Node.js最重要的支柱。
Node.js提供了require函數來調用其餘模塊,並且模塊都是基於文件的,機制十分簡單。
咱們常常把Node.js的模塊和包相提並論,由於模塊和包是沒有本質區別的,兩個概念也時常混淆。若是要辨析,那麼能夠把包理解成是實現了某個功能模塊的集合,用於發佈和維護。
1三、模塊是Node.js應用程序的基本組成部分,文件和模塊是一一對應的。換言之,一個Node.js文件就是一個模塊,這個文件多是JavaScript代碼、JSON或者編譯過的C/C++擴展。
例如:var http=require(‘http’),其中http就是Node.js的一個核心模塊,其內部是用C++實現的,外部用javaScript封裝。咱們經過require函數獲取這個模塊,而後才能使用其中的對象。
1四、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 提供的上萬個模
塊都是經過這種簡單的方式搭建起來的。
1五、單次加載
上面這個例子有點相似於建立一個對象,但實際上又和對象有本質區別,由於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覆蓋,最終輸入結果是由後者決定的。