簡單的說 Node.js 就是運作在服務端的 JavaScript。
Node.js 是一個基于Chrome JavaScript 運作時建立的一個平台。
Node.js是一個事件驅動I/O服務端JavaScript環境,基于Google的V8引擎,V8引擎執行Javascript的速度非常快,性能非常好。
。。。。。。
Node.js教程适用人群
對于不會運用Python、PHP以及Java等動态程式設計語言的前端程式員來說,選擇Node.js作為一個建立自己的服務的工具是非常明智的。
Node.js是運作在服務端的JavaScript,是以,熟悉Javascript的使用将有助于學習Node.js。
同時,學習該Node.js教程也可以幫助後端程式員部署一些高性能的服務。
安裝環境自己去配置,選擇自己需要的就可以了, node.js下載下傳位址:http://www.nodejs.org/en/download/
我們先了解下Node.js應用是由哪幾部分組成的:
引入required子產品:我們可以使用require指令來載入Node.js子產品。
http:/ /www.iis7.com/b/wzjk/
建立伺服器:伺服器可以監聽用戶端的請求,類似于Apache 、Nginx等HTTP伺服器。
接收請求與響應請求:伺服器很容易建立,用戶端可以使用浏覽器或終端發送HTTP請求,伺服器接收請求後傳回響應資料。
Node.js NPM(網絡性能監控器,node包管理器) 使用介紹
NPM是随同Node.js一起安裝的包管理工具,能解決Node.js代碼部署上的很多問題,常見的使用場景有以下幾種:
允許使用者從NPM伺服器下載下傳别人編寫的第三方包到本地使用。
允許使用者從NPM伺服器下載下傳并安裝别人編寫的指令行程式到本地使用。
允許使用者将自己編寫的包或指令行程式上傳到NPM伺服器供别人使用。
版本号::
使用NPM下載下傳和釋出代碼時都會接觸到版本号。NPM使用語義版本号來管理代碼,這裡簡單介紹一下。
語義版本号分為X.Y.Z三位,分别代表主版本号、次版本号和更新檔版本号。當代碼變更時,版本号按以下原則更新。
如果隻是修複bug,需要更新Z位。
如果是新增了功能,但是向下相容,需要更新Y位。
如果有大變動,向下不相容,需要更新X位。
版本号有了這個保證後,在申明第三方包依賴時,除了可依賴于一個固定版本号外,還可依賴于某個範圍的版本号。例如"argv": "0.0.x"表示依賴于0.0.x系列的最新版argv。
NPM常用指令::
NPM還提供了很多功能,package.json裡也有很多其它有用的字段。
除了可以在npmjs.org/doc/檢視官方文檔外,這裡再介紹一些NPM常用指令。
NPM提供了很多指令,例如install和publish,使用npm help可檢視所有指令。
使用npm help <command>可檢視某條指令的詳細幫助,例如npm help install。
在package.json所在目錄下使用npm install . -g可先在本地安裝目前指令行程式,可用于釋出前的本地測試。
使用npm update <package>可以把目前目錄下node_modules子目錄裡邊的對應子產品更新至最新版本。
使用npm update <package> -g可以把全局安裝的對應指令行程式更新至最新版。
使用npm cache clear可以清空NPM本地緩存,用于對付使用相同版本号釋出新版本代碼的人。
使用npm unpublish <package>@<version>可以撤銷釋出自己釋出過的某個版本代碼。
::::::::::::::::::::::::::::::::::::::::::::::::::::::
Node.js REPL(互動式解釋器)
Node.js REPL(Read Eval Print Loop:互動式解釋器) 表示一個電腦的環境,類似 Window 系統的終端或 Unix/Linux shell,我們可以在終端中輸入指令,并接收系統的響應。
REPL 的互動式的程式設計環境可以實時的驗證你所編寫的代碼,非常适合于驗證 Node.js 和 JavaScript 的相關 API。
Node 自帶了互動式解釋器,可以執行以下任務:
讀取 - 讀取使用者輸入,解析輸入了Javascript 資料結構并存儲在記憶體中。
執行 - 執行輸入的資料結構
列印 - 輸出結果
循環 - 循環操作以上步驟直到使用者兩次按下 ctrl-c 按鈕退出。
Node 的互動式解釋器可以很好的調試 Javascript 代碼。
我們可以輸入以下指令來啟動 Node 的終端:
$ node
這時我們就可以在 > 後輸入簡單的表達式,并按下Enter鍵來計算結果。 。。。 使用變量 你可以将資料存儲在變量中,并在你需要的使用它。 變量聲明需要使用 var 關鍵字,如果沒有使用 var 關鍵字變量會直接列印出來。 使用 var 關鍵字的變量可以使用 console.log() 來輸出變量。 多行表達式 Node REPL 支援輸入多行表達式,這就有點類似 JavaScript。接下來讓我們來執行一個 do-while 循環: var x = 0 undefined do { ... x++; ... console.log("x: " + x); ... } while ( x < 5 ); x: 1 x: 2 x: 3 x: 4 x: 5 ... 三個點的符号是系統自動生成的,你回車換行後即可。Node 會自動檢測是否為連續的表達式。 下劃線()變量 你可以使用下劃線()擷取表達式的運算結果: var x = 10 var y = 20 x + y 30 var sum = _ console.log(sum) REPL 指令 -ctrl + c - 退出目前終端。 -ctrl + c 按下兩次 - 退出 Node REPL。 -ctrl + d - 退出 Node REPL. -向上/向下 鍵 - 檢視輸入的曆史指令 -tab 鍵 - 列出目前指令 -.help - 列出使用指令 -.break - 退出多行表達式 -.clear - 退出多行表達式 -.save filename - 儲存目前的 Node REPL 會話到指定檔案 -.load filename - 載入目前 Node REPL 會話的檔案内容。 停止 REPL 前面我們已經提到按下兩次 ctrl + c 建就能退出 REPL: (^C again to quit) ::::::::::::::::::::::::::::::::::::::: Node.js 回調函數 Node.js 異步程式設計的直接展現就是回調。 異步程式設計依托于回調來實作,但不能說使用了回調後程式就異步化了。 回調函數在完成任務後就會被調用,Node 使用了大量的回調函數,Node 所有 API 都支援回調函數。 例如,我們可以一邊讀取檔案,一邊執行其他指令,在檔案讀取完成後,我們将檔案内容作為回調函數的參數傳回。這樣在執行代碼時就沒有阻塞或等待檔案 I/O 操作。這就大大提高了 Node.js 的性能,可以處理大量的并發請求。 阻塞代碼執行個體 建立一個檔案 input.txt ,内容如下: wodema位址:www.wodema.cn 建立 main.js 檔案, 代碼如下: var fs = require("fs");
var data = fs.readFileSync('input.txt');
console.log(data.toString());
console.log("程式執行結束!");
以上代碼執行結果如下:
$ node main.js
我的媽位址:www.wodema.cn
程式執行結束!
非阻塞代碼執行個體
fs.readFile('input.txt', function (err, data) {
if (err) return console.error(err);
});
以上兩個執行個體我們了解了阻塞與非阻塞調用的不同。第一個執行個體在檔案讀取完後才執行完程式。 第二個執行個體我們呢不需要等待檔案讀取完,這樣就可以在讀取檔案時同時執行接下來的代碼,大大提高了程式的性能。
是以,阻塞按是按順序執行的,而非阻塞是不需要按順序的,是以如果需要處理回調函數的參數,我們就需要寫在回調函數内。
:::::::::::::::::::::::::::::::::
Node.js 事件循環
Node.js 是單程序單線程應用程式,但是通過事件和回調支援并發,是以性能非常高。
Node.js 的每一個 API 都是異步的,并作為一個獨立線程運作,使用異步函數調用,并處理并發。
Node.js 基本上所有的事件機制都是用設計模式中觀察者模式實作。
Node.js 單線程類似進入一個while(true)的事件循環,直到沒有事件觀察者退出,每個異步事件都生成一個事件觀察者,如果有事件發生就調用該回調函數.
事件驅動程式
Node.js 使用事件驅動模型,當web server接收到請求,就把它關閉然後進行處理,然後去服務下一個web請求。
當這個請求完成,它被放回處理隊列,當到達隊列開頭,這個結果被傳回給使用者。
這個模型非常高效可擴充性非常強,因為webserver一直接受請求而不等待任何讀寫操作。(這也被稱之為非阻塞式IO或者事件驅動IO)
在事件驅動模型中,會生成一個主循環來監聽事件,當檢測到事件時觸發回調函數。
整個事件驅動的流程就是這麼實作的,非常簡潔。有點類似于觀察者模式,事件相當于一個主題(Subject),而所有注冊到這個事件上的處理函數相當于觀察者(Observer)。
Node.js 有多個内置的事件,我們可以通過引入 events 子產品,并通過執行個體化 EventEmitter 類來綁定和監聽事件,如下執行個體:
// 引入 events 子產品
var events = require('events');
// 建立 eventEmitter 對象
var eventEmitter = new events.EventEmitter();
以下程式綁定事件處理程式:
// 綁定事件及事件的處理程式
eventEmitter.on('eventName', eventHandler);
我們可以通過程式觸發事件:
// 觸發事件
eventEmitter.emit('eventName');
Node 應用程式是如何工作的?
在 Node 應用程式中,執行異步操作的函數将回調函數作為最後一個參數, 回調函數接收錯誤對象作為第一個參數。
接下來讓我們來重新看下前面的執行個體,建立一個 input.txt ,檔案内容如下:123456789位址:www.123456789.cn
::::::::::::::::::::::::::::::::
Node.js 事件
Node.js 所有的異步 I/O 操作在完成時都會發送一個事件到事件隊列。
Node.js 裡面的許多對象都會分發事件:一個net.Server對象會在每次有新連接配接時分發一個事件, 一個fs.readStream對象會在檔案被打開的時候發出一個事件。 所有這些産生事件的對象都是 events.EventEmitter 的執行個體。 你可以通過require("events");來通路該子產品。
EventEmitter介紹
events 子產品隻提供了一個對象: events.EventEmitter。EventEmitter 的核心就 是事件發射與事件監聽器功能的封裝。
EventEmitter 的每個事件由一個事件名和若幹個參 數組成,事件名是一個字元串,通常表達一定的語義。對于每個事件,EventEmitter 支援 若幹個事件監聽器。
當事件發射時,注冊到這個事件的事件監聽器被依次調用,事件參數作 為回調函數參數傳遞。
EventEmitter常用的API
EventEmitter.on(event, listener)、emitter.addListener(event, listener) 為指定事件注冊一個監聽器,接收一個字元串 event 和一個回調函數 listener。
server.on('connection', function (stream) {
console.log('someone connected!');
EventEmitter.emit(event, [arg1], [arg2], [...]) 發射 event 事件,傳 遞若幹可選參數到事件監聽器的參數表。
EventEmitter.once(event, listener) 為指定事件注冊一個單次監聽器,即 監聽器最多隻會觸發一次,觸發後立刻解除該監聽器。
server.once('connection', function (stream) {
console.log('Ah, we have our first user!');
EventEmitter.removeListener(event, listener) 移除指定事件的某個監聽 器,listener 必須是該事件已經注冊過的監聽器。
var callback = function(stream) {
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
EventEmitter.removeAllListeners([event]) 移除所有事件的所有監聽器, 如果指定 event,則移除指定事件的所有監聽器。
error 事件
EventEmitter 定義了一個特殊的事件 error,它包含了"錯誤"的語義,我們在遇到 異常的時候通常會發射 error 事件。
當 error 被發射時,EventEmitter 規定如果沒有響 應的監聽器,Node.js 會把它當作異常,退出程式并列印調用棧。
我們一般要為會發射 error 事件的對象設定監聽器,避免遇到錯誤後整個程式崩潰。例如:
var emitter = new events.EventEmitter();
emitter.emit('error');
繼承 EventEmitter
大多數時候我們不會直接使用 EventEmitter,而是在對象中繼承它。包括 fs、net、 http 在内的,隻要是支援事件響應的核心子產品都是 EventEmitter 的子類。
為什麼要這樣做呢?原因有兩點:
首先,具有某個實體功能的對象實作事件符合語義, 事件的監聽和發射應該是一個對象的方法。
其次JavaScript 的對象機制是基于原型的,支援 部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關系。
::::::::::::::::::::::::::::::::::::::::
Node.js Buffer(緩沖區)
JavaScript 語言自身隻有字元串資料類型,沒有二進制資料類型。
但在處理像TCP流或檔案流時,必須使用到二進制資料。是以在 Node.js中,定義了一個 Buffer 類,該類用來建立一個專門存放二進制資料的緩存區。
在 Node.js 中,Buffer 類是随 Node 核心一起釋出的核心庫。Buffer 庫為 Node.js 帶來了一種存儲原始資料的方法,可以讓 Node.js 處理二進制資料,每當需要在 Node.js 中處理I/O操作中移動的資料時,就有可能使用 Buffer 庫。原始資料存儲在 Buffer 類的執行個體中。一個 Buffer 類似于一個整數數組,但它對應于 V8 堆記憶體之外的一塊原始記憶體。
建立 Buffer 類
Node Buffer 類可以通過多種方式來建立。
方法 1
建立長度為 10 位元組的 Buffer 執行個體:
var buf = new Buffer(10);
方法 2
通過給定的數組建立 Buffer 執行個體:
var buf = new Buffer([10, 20, 30, 40, 50]);
方法 3
通過一個字元串來建立 Buffer 執行個體:
var buf = new Buffer("www.123456789.cn", "utf-8");
utf-8 是預設的編碼方式,此外它同樣支援以下編碼:"ascii", "utf8", "utf16le", "ucs2", "base64" 和 "hex"。
寫入緩沖區
文法
寫入 Node 緩沖區的文法如下所示:
buf.write(string[, offset[, length]][, encoding])
參數
參數描述如下:
-string - 寫入緩沖區的字元串。
-offset - 緩沖區開始寫入的索引值,預設為 0 。
-length - 寫入的位元組數,預設為 buffer.length
-encoding - 使用的編碼。預設為 'utf8' 。
傳回值
傳回實際寫入的大小。如果 buffer 空間不足, 則隻會寫入部分字元串。
從緩沖區讀取資料
讀取 Node 緩沖區資料的文法如下所示:
buf.toString([encoding[,start[,end]]])
-start - 指定開始讀取的索引位置,預設為 0。
-end - 結束位置,預設為緩沖區的末尾。
解碼緩沖區資料并使用指定的編碼傳回字元串。
将 Buffer 轉換為 JSON 對象
将 Node Buffer 轉換為 JSON 對象的函數文法格式如右:buf.toJSON()
傳回 JSON 對象。
緩沖區合并
Node 緩沖區合并的文法如下所示:
Buffer.concat(list[, totalLength])
-list - 用于合并的 Buffer 對象數組清單。
-totalLength - 指定合并後Buffer對象的總長度。
傳回一個多個成員合并的新 Buffer 對象。
緩沖區比較
Node Buffer 比較的函數文法如下所示, 該方法在 Node.js v0.12.2 版本引入:
buf.compare(otherBuffer);
otherBuffer - 與 buf 對象比較的另外一個 Buffer 對象。
傳回一個數字,表示 buf 在 otherBuffer 之前,之後或相同。
拷貝緩沖區
Node 緩沖區拷貝文法如下所示:
buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
-targetBuffer - 要拷貝的 Buffer 對象。
-targetStart - 數字, 可選, 預設: 0
-sourceStart - 數字, 可選, 預設: 0
-sourceEnd - 數字, 可選, 預設: buffer.length
沒有傳回值。
緩沖區裁剪
Node 緩沖區裁剪文法如下所示:
buf.slice([start[, end]])
-start - 數字, 可選, 預設: 0
-end - 數字, 可選, 預設: buffer.length
傳回一個新的緩沖區,它和舊緩沖區指向同一塊記憶體,但是從索引 start 到 end 的位置剪切。
緩沖區長度
Node 緩沖區長度計算文法如下所示:
buf.length;
傳回 Buffer 對象所占據的記憶體長度。
:::::::::::::::::::::::::::::::::::::::::::
Node.js Stream(流)
Stream 是 Node.js 中非常重要的一個子產品,應用廣泛。
Stream 是一個抽象接口,Node 中有很多對象實作了這個接口。例如,對http 伺服器發起請求的request 對象就是一個 Stream,還有stdout(标準輸出)。
該抽象接口是可讀、可寫或是既可讀又可寫的,通過這些接口,我們可以和磁盤檔案、套接字、HTTP請求來互動,實作資料從一個地方流動到另一個地方的功能。
Node.js,Stream 有四種流類型:
-Readable - 可讀操作。
-Writable - 可寫操作。
-Duplex - 可讀可寫操作.
-Transform - 操作被寫入資料,然後讀出結果。
所有的 Stream 對象都是 EventEmitter 的執行個體。常用的事件有:
-data - 當有資料可讀時觸發。
-end - 沒有更多的資料可讀時觸發。
-error - 在接收和寫入過程中發生錯誤時觸發。
-finish - 所有資料已被寫入到底層系統時觸發。
管道流
管道提供了一個輸出流到輸入流的機制。通常我們用于從一個流中擷取資料并将資料傳遞到另外一個流中。
鍊式流
鍊式是通過連接配接輸出流到另外一個流并建立多個對個流操作鍊的機制。鍊式流一般用于管道操作。
接下來我們就是用管道和鍊式來壓縮和解壓檔案。
::::::::::::::::::::::::::::::::::::::::::
Node.js子產品系統
為了讓Node.js的檔案可以互相調用,Node.js提供了一個簡單的子產品系統。
子產品是Node.js 應用程式的基本組成部分,檔案和子產品是一一對應的。換言之,一個 Node.js 檔案就是一個子產品,這個檔案可能是JavaScript 代碼、JSON 或者編譯過的C/C++ 擴充。
建立子產品
在 Node.js 中,建立一個子產品非常簡單,如下我們建立一個 'main.js' 檔案,代碼如下:
var hello = require('./hello');
hello.world();
以上執行個體中,代碼 require('./hello') 引入了目前目錄下的hello.js檔案(./ 為目前目錄,node.js預設字尾為js)。
Node.js 提供了exports 和 require 兩個對象,其中 exports 是子產品公開的接口,require 用于從外部擷取一個子產品的接口,即所擷取子產品的 exports 對象。
服務端的子產品放在哪裡
也許你已經注意到,我們已經在代碼中使用了子產品了。像這樣:
var http = require("http");
...
http.createServer(...);
Node.js中自帶了一個叫做"http"的子產品,我們在我們的代碼中請求它并把傳回值賦給一個本地變量。
這把我們的本地變量變成了一個擁有所有 http 子產品所提供的公共方法的對象。
Node.js 的 require方法中的檔案查找政策如下:
由于Node.js中存在4類子產品(原生子產品和3種檔案子產品),盡管require方法極其簡單,但是内部的加載卻是十分複雜的,其加載優先級也各自不同。
從檔案子產品緩存中加載
盡管原生子產品與檔案子產品的優先級不同,但是都不會優先于從檔案子產品的緩存中加載已經存在的子產品。
從原生子產品加載
原生子產品的優先級僅次于檔案子產品緩存的優先級。require方法在解析檔案名之後,優先檢查子產品是否在原生子產品清單中。以http子產品為例,盡管在目錄下存在一個http/http.js/http.node/http.json檔案,require("http")都不會從這些檔案中加載,而是從原生子產品中加載。
原生子產品也有一個緩存區,同樣也是優先從緩存區加載。如果緩存區沒有被加載過,則調用原生子產品的加載方式進行加載和執行。
從檔案加載
當檔案子產品緩存中不存在,而且不是原生子產品的時候,Node.js會解析require方法傳入的參數,并從檔案系統中加載實際的檔案,加載過程中的包裝和編譯細節在前一節中已經介紹過,這裡我們将較長的描述查找檔案子產品的過程,其中,也有一些細節值得知曉。
require方法接受以下幾種參數的傳遞:
-http、fs、path等,原生子產品。
-./mod或../mod,相對路徑的檔案子產品。
-/pathtomodule/mod,絕對路徑的檔案子產品。
-mod,非原生子產品的檔案子產品。
Node.js 函數
在JavaScript中,一個函數可以作為另一個函數接收一個參數。我們可以先定義一個函數,然後傳遞,也可以在傳遞參數的地方直接定義函數。
Node.js中函數的使用與Javascript類似,舉例來說,你可以這樣做:
function say(word) {
console.log(word);
}
function execute(someFunction, value) {
someFunction(value);
execute(say, "Hello");
以上代碼中,我們把 say 函數作為execute函數的第一個變量進行了傳遞。這裡傳回的不是 say 的傳回值,而是 say 本身!
這樣一來, say 就變成了execute 中的本地變量 someFunction ,execute可以通過調用 someFunction() (帶括号的形式)來使用 say 函數。
當然,因為 say 有一個變量, execute 在調用 someFunction 時可以傳遞這樣一個變量。
匿名函數
我們可以把一個函數作為變量傳遞。但是我們不一定要繞這個"先定義,再傳遞"的圈子,我們可以直接在另一個函數的括号中定義和傳遞這個函數:
execute(function(word){ console.log(word) }, "Hello");
我們在 execute 接受第一個參數的地方直接定義了我們準備傳遞給 execute 的函數。
用這種方式,我們甚至不用給這個函數起名字,這也是為什麼它被叫做匿名函數 。
函數傳遞是如何讓HTTP伺服器工作的
帶着這些知識,我們再來看看我們簡約而不簡單的HTTP伺服器:
http.createServer(function(request, response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}).listen(8888);
現在它看上去應該清晰了很多:我們向 createServer 函數傳遞了一個匿名函數。
用這樣的代碼也可以達到同樣的目的:
function onRequest(request, response) {
http.createServer(onRequest).listen(8888);
::::::::::::::::::::::::::::::::::::::::::::::
Node.js 路由
我們要為路由提供請求的URL和其他需要的GET及POST參數,随後路由需要根據這些資料來執行相應的代碼。
是以,我們需要檢視HTTP請求,從中提取出請求的URL以及GET/POST參數。這一功能應當屬于路由還是伺服器(甚至作為一個子產品自身的功能)确實值得探讨,但這裡暫定其為我們的HTTP伺服器的功能。
我們需要的所有資料都會包含在request對象中,該對象作為onRequest()回調函數的第一個參數傳遞。但是為了解析這些資料,我們需要額外的Node.JS子產品,它們分别是url和querystring子產品。
當然我們也可以用querystring子產品來解析POST請求體中的參數,稍後會有示範。
現在我們來給onRequest()函數加上一些邏輯,用來找出浏覽器請求的URL路徑:
var url = require("url");
function start() {
var pathname = url.parse(request.url).pathname;
console.log("Request for " + pathname + " received.");
console.log("Server has started.");
exports.start = start;
好了,我們的應用現在可以通過請求的URL路徑來差別不同請求了--這使我們得以使用路由(還未完成)來将請求以URL路徑為基準映射到處理程式上。
在我們所要建構的應用中,這意味着來自/start和/upload的請求可以使用不同的代碼來處理。稍後我們将看到這些内容是如何整合到一起的。
現在我們可以來編寫路由了,建立一個名為router.js的檔案,添加以下内容:
function route(pathname) {
console.log("About to route a request for " + pathname);
exports.route = route;
如你所見,這段代碼什麼也沒幹,不過對于現在來說這是應該的。在添加更多的邏輯以前,我們先來看看如何把路由和伺服器整合起來。
我們的伺服器應當知道路由的存在并加以有效利用。我們當然可以通過寫死的方式将這一依賴項綁定到伺服器上,但是其它語言的程式設計經驗告訴我們這會是一件非常痛苦的事,是以我們将使用依賴注入的方式較松散地添加路由子產品。
:::::::::::::::::::::::::::::::::::::::::
Node.js 全局對象
現在介紹 Node.js 全局對象,global 全局對象無需引用就可以直接使用。
JavaScript 中有一個特殊的對象,稱為全局對象(Global Object),它及其所有屬性都可以在程式的任何地方通路,即全局變量。
在浏覽器 JavaScript 中,通常window 是全局對象, 而Node.js 中的全局對象是 global,所有全局變量(除了 global 本身以外)都是 global 對象的屬性。
我們在Node.js 中能夠直接通路到對象通常都是 global 的屬性,如 console、process 等,
全局對象與全局變量
global 最根本的作用是作為全局變量的宿主。按照 ECMAScript 的定義,滿足以下條件的變量是全局變量:
在最外層定義的變量;
全局對象的屬性;
隐式定義的變量(未定義直接指派的變量)。
當你定義一個全局變量時,這個變量同時也會成為全局對象的屬性,反之亦然。需要注 意的是,在Node.js 中你不可能在最外層定義變量,因為所有使用者代碼都是屬于目前子產品的, 而子產品本身不是最外層上下文。
注意: 永遠使用var 定義變量以避免引入全局變量,因為全局變量會污染 命名空間,提高代碼的耦合風險。
process
process 是一個全局變量,即 global 對象的屬性。
它用于描述目前Node.js 程序狀态 的對象,提供了一個與作業系統的簡單接口。通常在你寫本地指令行程式的時候,少不了要 和它打交道。下面将會介紹process 對象的一些最常用的成員方法。
process.argv是指令行參數數組,第一個元素是 node,第二個元素是腳本檔案名, 從第三個元素開始每個元素是一個運作參數。
console.log(process.argv);
将以上代碼存儲為argv.js,通過以下指令運作:
$ node argv.js 1991 name=byvoid --v "Carbo Kuo"
[ 'node',
'/home/byvoid/argv.js',
'1991',
'name=byvoid',
'--v',
'Carbo Kuo' ]
process.stdout是标準輸出流,通常我們使用的 console.log() 向标準輸出列印 字元,而 process.stdout.write() 函數提供了更底層的接口。
process.stdin是标準輸入流,初始時它是被暫停的,要想從标準輸入讀取資料, 你必須恢複流,并手動編寫流的事件響應函數。
process.stdin.resume();
process.stdin.on('data', function(data) {
process.stdout.write('read from console: ' + data.toString());
process.nextTick(callback)的功能是為事件循環設定一項任務,Node.js 會在 下次事件循環調響應時調用 callback。
初學者很可能不了解這個函數的作用,有什麼任務不能在當下執行完,需要交給下次事 件循環響應來做呢?
我們讨論過,Node.js 适合I/O 密集型的應用,而不是計算密集型的應用, 因為一個Node.js 程序隻有一個線程,是以在任何時刻都隻有一個事件在執行。
如果這個事 件占用大量的CPU 時間,執行事件循環中的下一個事件就需要等待很久,是以Node.js 的一 個程式設計原則就是盡量縮短每個事件的執行時間。process.nextTick() 提供了一個這樣的 工具,可以把複雜的工作拆散,變成一個個較小的事件。
functiondoSomething(args, callback) {
somethingComplicated(args);
callback();
doSomething(functiononEnd() {
compute();
我們假設compute() 和somethingComplicated() 是兩個較為耗時的函數,以上 的程式在調用 doSomething() 時會先執行somethingComplicated(),然後立即調用 回調函數,在 onEnd() 中又會執行 compute()。下面用process.nextTick() 改寫上 面的程式:
process.nextTick(callback);
改寫後的程式會把上面耗時的操作拆分為兩個事件,減少每個事件的執行時間,提高事 件響應速度。
注意: 不要使用setTimeout(fn,0)代替process.nextTick(callback), 前者比後者效率要低得多。
我們探讨了process對象常用的幾個成員,除此之外process還展示了process.platform、 process.pid、process.execPath、process.memoryUsage() 等方法,以及POSIX 程序信号響應機制。有興趣的讀者可以通路http://nodejs.org/api/process.html 了解詳細 内容。
console
console 用于提供控制台标準輸出,它是由Internet Explorer 的JScript 引擎提供的調試 工具,後來逐漸成為浏覽器的事實标準。
Node.js 沿用了這個标準,提供與習慣行為一緻的 console 對象,用于向标準輸出流(stdout)或标準錯誤流(stderr)輸出字元。 ? console.log():向标準輸出流列印字元并以換行符結束。
console.log 接受若幹 個參數,如果隻有一個參數,則輸出這個參數的字元串形式。如果有多個參數,則 以類似于C 語言 printf() 指令的格式輸出。
第一個參數是一個字元串,如果沒有 參數,隻列印一個換行。
console.error():與console.log() 用法相同,隻是向标準錯誤流輸出。
console.trace():向标準錯誤流輸出目前的調用棧。
:::::::::::::::::::::::::::::::::::::
Node.js 常用工具 util
現在介紹Node.js常用工具util。
util作為Node.js的一個核心子產品,能夠提供常用函數的集合,彌補核心JavaScript的功能過于精簡的不足。
util.inherits
util.inherits(constructor, superConstructor)是一個實作對象間原型繼承的函數。
與常見的基于類的不同,JavaScript的面向對象特性是基于原型的。JavaScript沒有提供對象繼承的語言級别特性,而是通過原型複制來實作的。
在這裡我們隻介紹util.inherits的用法
注意:Sub僅僅繼承了Base在原型中定義的函數,而構造函數内部創造的base屬性和sayHello函數都沒有被Sub繼承。
同時,在原型中定義的屬性不會被console.log作為對象的屬性輸出。如果我們去掉objSub.sayHello(); 這行的注釋,
util.inspect
util.inspect(object,[showHidden],[depth],[colors])方法可以将任意對象轉換為字元串,通常用于調試和錯誤輸出。它至少接受一個object參數,即要轉換的對象。
showHidden是一個可選參數,如果值為true,将會輸出更多隐藏資訊。
depth表示最大遞歸的層數,如果對象很複雜,你可以指定層數以控制輸出資訊的多少。如果不指定depth,則預設遞歸2層,指定為null時表示将不限遞歸層數完整周遊對象。 如果color值為true,則輸出格式将會以ANSI顔色編碼,通常用于在終端顯示更漂亮的效果。
特别要指出的是,util.inspect并不會簡單地直接把對象轉換為字元串,即使該對象定義了toString方法也不會調用。
util.isArray(object)
如果給定的參數 "object" 是一個數組傳回true,否則傳回false。
util.isRegExp(object)
如果給定的參數"object"是一個正規表達式傳回true,否則傳回false。
util.isDate(object)
如果給定的參數 "object" 是一個日期傳回true,否則傳回false。
util.isError(object)
如果給定的參數 "object" 是一個錯誤對象傳回true,否則傳回false。
::::::::::::::::::::::::::::::::::::::
Node.js 檔案系統
Node.js檔案系統被封裝在fs子產品中,它提供了檔案的讀取、寫入、更名、删除、周遊目錄、連結等POSIX檔案系統操作。
與其他子產品不同的是,fs子產品中所有的操作都提供了異步的和同步的兩個版本,例如讀取檔案内容的函數有異步的fs.readFile()和同步的fs.readFileSync()。我們以幾個函數為代表,介紹fs常用的功能,并列出fs所有函數的定義和功能。
fs.readFile
Node.js讀取檔案函數文法如下:
-fs.readFile(filename,[encoding],[callback(err,data)])
-filename(必選),表示要讀取的檔案名。
-encoding(可選),表示檔案的字元編碼。
-callback 是回調函數,用于接收檔案的内容。
如果不指定encoding,則callback就是第二個參數。回調函數提供兩個參數err和data,err表示有沒有錯誤發生,data是檔案内容。如果指定了encoding,data是一個解析後的字元串,否則data将會是以Buffer形式表示的二進制資料。
fs.readFileSync
fs.readFileSync(filename, [encoding])是fs.readFile同步的版本。它接受和 fs.readFile 相同的參數,而讀取到的檔案内容會以函數傳回值的形式傳回。如果有錯 誤發生,fs将會抛出異常,你需要使用try和catch捕捉并處理異常。
注意:與同步I/O函數不同,Node.js中異步函數大多沒有傳回值。
fs.open
fs.open(path, flags, [mode], [callback(err, fd)])是POSIX open函數的封裝,類似于C語言标準庫中的fopen函數。它接受兩個必選參數,path為檔案的路徑, flags 可以是以下值:
r :以讀取模式打開檔案。
r+ :以讀寫模式打開檔案。
w :以寫入模式打開檔案,如果檔案不存在則建立。
w+ :以讀寫模式打開檔案,如果檔案不存在則建立。
a :以追加模式打開檔案,如果檔案不存在則建立。
a+ :以讀取追加模式打開檔案,如果檔案不存在則建立
fs.read
fs.read文法格式如下:
fs.read(fd, buffer, offset, length, position, [callback(err, bytesRead, buffer)])
參數說明:
-fd: 讀取資料并寫入buffer指向的緩沖區對象。
-offset: 是buffer的寫入偏移量。
-length: 是要從檔案中讀取的位元組數。
-position: 是檔案讀取的起始位置,如果position的值為null,則會從目前檔案指針的位置讀取。
-callback:回調函數傳遞bytesRead和buffer,分别表示讀取的位元組數和緩沖區對象。
::::::::::::::::::::::::::::::::::::::::::::
Node.js 工具子產品
在 Node.js 子產品庫中有很多好用的子產品。這些子產品都是很常見的,并同時開發基于任何節點的應用程式頻繁使用。接下來我們為大家介紹幾種常用子產品的使用:
序号 子產品名 描述
1 OS 子產品 提供基本的系統操作函數。
2 Path 子產品 提供了處理和轉換檔案路的工具。
3 Net 子產品 用于底層的網絡通信。提供了服務端和用戶端的的操作。
4 DNS 子產品 用于解析域名。
5 Domain 子產品 簡化異步代碼的異常處理,可以捕捉處理try catch無法捕捉的。
以上就是常用的Node.js工具子產品
Node.js Web 子產品
現在介紹Node.js Web子產品,首先,你應該先了解什麼是Web伺服器。
什麼是 Web 伺服器?
Web伺服器一般指網站伺服器,是指駐留于網際網路上某種類型計算機的程式。
Web伺服器的基本功能就是提供Web資訊浏覽服務。它隻需支援HTTP協定、HTML文檔格式及URL,與用戶端的網絡浏覽器配合。
大多數web伺服器都支援服務端的腳本語言(php、python、ruby)等,并通過腳本語言從資料庫擷取資料,将結果傳回給用戶端浏覽器。
目前最主流的三個Web伺服器是Apache、Nginx、IIS。
-Client - 用戶端,一般指浏覽器,浏覽器可以通過HTTP協定向伺服器請求資料。
-Server - 服務端,一般指Web伺服器,可以接收用戶端請求,并向用戶端發送響應資料。
-Business - 業務層, 通過Web伺服器處理應用程式,如與資料庫互動,邏輯運算,調用外部程式等。
-Data - 資料層,一般由資料庫組成。
使用 Node 建立 Web 伺服器
Node.js提供了http子產品,http子產品主要用于搭建HTTP服務端和用戶端,如果要使用HTTP伺服器或用戶端功能,則必須調用http子產品,代碼如下:
var http = require('http');
使用 Node 建立 Web 用戶端
使用Node建立Web用戶端需要引入http子產品,建立client.js檔案,代碼如下所示:
// 用于請求的選項
var options = {
host: 'localhost',
port: '8081',
path: '/index.htm'
// 處理響應的回調函數
var callback = function(response){
// 不斷更新資料
var body = '';
response.on('data', function(data) {
body += data;
response.on('end', function() {
// 資料接收完成
console.log(body);
// 向服務端發送請求
var req = http.request(options, callback);
req.end();
::::::::::::::::::::::::::::::::::
Node.js Express 架構
Express 是一個為Node.js設計的web開發架構,它基于nodejs平台。
Express 簡介
Express是一個簡潔而靈活的node.js Web應用架構, 提供了一系列強大特性幫助你建立各種Web應用,和豐富的HTTP工具。
使用Express可以快速地搭建一個完整功能的網站。
Express 架構核心特性包括:
-可以設定中間件來響應HTTP請求。
-定義了路由表用于執行不同的HTTP請求動作。
-可以通過向模闆傳遞參數來動态渲染HTML頁面。
安裝 Express
安裝Express并将其儲存到依賴清單中:
$ npm install express --save
以上指令會将Express架構安裝在當期目錄的node_modules目錄中, node_modules目錄下會自動建立express目錄。以下幾個重要的子產品是需要與express架構一起安裝的:
-body-parser - node.js中間件,用于處理JSON, Raw, Text和URL編碼的資料。
-cookie-parser - 這就是一個解析Cookie的工具。通過req.cookies可以取到傳過來的cookie,并把它們轉成對象。
-multer - node.js中間件,用于處理enctype="multipart/form-data"(設定表單的MIME編碼)的表單資料。
第一個 Express 架構執行個體
接下來我們使用Express架構來輸出"Hello World"。
以下執行個體中我們引入了express子產品,并在用戶端發起請求後,響應"Hello World"字元串。
建立express_demo.js檔案
請求和響應
Express應用使用回調函數的參數: request和response對象來處理請求和響應的資料。
app.get('/', function (req, res) {
// --
})
request和response對象的具體介紹:
Request 對象 - request對象表示HTTP請求,包含了請求查詢字元串,參數,内容,HTTP頭部等屬性。常見屬性有:
-req.app:當callback為外部檔案時,用req.app通路express的執行個體
-req.baseUrl:擷取路由目前安裝的URL路徑
-req.body / req.cookies:獲得「請求主體」/ Cookies
-req.fresh / req.stale:判斷請求是否還「新鮮」
-req.hostname / req.ip:擷取主機名和IP位址
-req.originalUrl:擷取原始請求URL
-req.params:擷取路由的parameters
-req.path:擷取請求路徑
-req.protocol:擷取協定類型
-req.query:擷取URL的查詢參數串
-req.route:擷取目前比對的路由
-req.subdomains:擷取子域名
-req.accpets():檢查請求的Accept頭的請求類型
-req.acceptsCharsets / req.acceptsEncodings / req.acceptsLanguages
-req.get():擷取指定的HTTP請求頭
-req.is():判斷請求頭Content-Type的MIME類型
Response 對象 - response對象表示HTTP響應,即在接收到請求時向用戶端發送的HTTP響應資料。常見屬性有:
-res.app:同req.app一樣
-res.append():追加指定HTTP頭
-res.set()在res.append()後将重置之前設定的頭
-res.cookie(name,value [,option]):設定Cookie
-opition: domain / expires / httpOnly / maxAge / path / secure / signed
-res.clearCookie():清除Cookie
-res.download():傳送指定路徑的檔案
-res.get():傳回指定的HTTP頭
-res.json():傳送JSON響應
-res.jsonp():傳送JSONP響應
-res.location():隻設定響應的Location HTTP頭,不設定狀态碼或者close response
-res.redirect():設定響應的Location HTTP頭,并且設定狀态碼302
-res.send():傳送HTTP響應
-res.sendFile(path [,options] [,fn]):傳送指定路徑的檔案 -會自動根據檔案extension設定Content-Type
-res.set():設定HTTP頭,傳入object可以一次設定多個頭
-res.status():設定HTTP狀态碼
-res.type():設定Content-Type的MIME類型
路由
我們已經了解了HTTP請求的基本應用,而路由決定了由誰(指定腳本)去響應用戶端請求。
在HTTP請求中,我們可以通過路由提取出請求的URL以及GET/POST參數。
接下來我們擴充Hello World,添加一些功能來處理更多類型的HTTP請求。
建立express_demo2.js檔案
靜态檔案
Express提供了内置的中間件express.static來設定靜态檔案如:圖檔,CSS, JavaScript等。
你可以使用express.static中間件來設定靜态檔案路徑。例如,如果你将圖檔, CSS, JavaScript檔案放在public目錄下,你可以這麼寫:
app.use(express.static('public'));
我們可以到public/images目錄下放些圖檔,如下所示:
node_modules
server.js
public/
public/images
public/images/logo.png
讓我們再修改下"Hello Word"應用添加處理靜态檔案的功能。
GET 方法
在表單中通過GET方法送出兩個參數,我們可以使用server.js檔案内的process_get路由器來處理輸入
POST 方法
在表單中通過POST方法送出兩個參數,我們可以使用server.js檔案内的process_post路由器來處理輸入
檔案上傳
建立一個用于上傳檔案的表單,使用POST方法,表單enctype屬性設定為multipart/form-data。
Cookie 管理
我們可以使用中間件向Node.js伺服器發送cookie資訊
:::::::::::::::::::::::::::::::::::
Node.js RESTful API
現在介紹Node.js的RESTful API。
什麼是 REST?
REST中文解釋為,表述性狀态傳遞(英文:Representational State Transfer,簡稱REST),是Roy Fielding博士在2000年他的博士論文中提出來的一種軟體架構風格。
表述性狀态轉移是一組架構限制條件和原則。滿足這些限制條件和原則的應用程式或設計就是RESTful。
需要注意的是,REST是設計風格而不是标準。REST通常基于使用HTTP,URI和XML(标準通用标記語言下的一個子集)以及HTML(标準通用标記語言下的一個應用)這些現有的廣泛流行的協定和标準。REST通常使用JSON資料格式。
HTTP 方法
以下為REST基本架構的四個方法:
-GET - 用于擷取資料。
-PUT - 用于添加資料。
-DELETE - 用于删除資料。
-POST - 用于更新或添加資料。
RESTful Web Services
Webservice是一個平台獨立的,低耦合的,自包含的、基于可程式設計的web的應用程式,可使用開放的XML(标準通用标記語言下的一個子集)标準來描述、釋出、發現、協調和配置這些應用程式,用于開發分布式的互操作的應用程式。
RESTful是基于REST架構的Web Services。
由于輕量級以及通過HTTP直接傳輸資料的特性,Web服務的RESTful方法已經成為最常見的替代方法。可以使用各種語言(比如,Java程式、Perl、Ruby、Python、PHP和Javascript[包括Ajax])實作用戶端。
RESTful Web服務通常可以通過自動用戶端或代表使用者的應用程式通路。但是,這種服務的簡便性讓使用者能夠與之直接互動,使用它們的Web浏覽器建構一個GET URL并讀取傳回的内容。
:::::::::::::::::::::::::::::::::::::::::::::::::::
Node.js 多程序
Node.js本身是以單線程的模式運作的,但它使用的是事件驅動來處理并發,這樣有助于我們在多核 cpu 的系統上建立多個子程序,進而提高性能。
每個子程序總是帶有三個流對象:child.stdin, child.stdout和child.stderr。他們可能會共享父程序的stdio流,或者也可以是獨立的被導流的流對象。
Node提供了child_process子產品來建立子程序,方法有:
-exec - child_process.exec使用子程序執行指令,緩存子程序的輸出,并将子程序的輸出以回調函數參數的形式傳回。
-spawn - child_process.spawn使用指定的指令行參數建立新程序。
-fork - child_process.fork是spawn()的特殊形式,用于在子程序中運作的子產品,如fork('./son.js')相當于spawn('node', ['./son.js']) 。與spawn方法不同的是,fork會在父程序與子程序之間,建立一個通信管道,用于程序之間的通信。
exec() 方法
child_process.exec使用子程序執行指令,緩存子程序的輸出,并将子程序的輸出以回調函數參數的形式傳回。
文法如下所示:
child_process.exec(command[, options], callback)
參數說明如下:
-command: 字元串, 将要運作的指令,參數使用空格隔開
-options :對象,可以是:
-cwd,字元串,子程序的目前工作目錄
-env,對象,環境變量鍵值對
-encoding,字元串,字元編碼(預設: 'utf8')
-shell,字元串,将要執行指令的Shell(預設: 在UNIX中為/bin/sh, 在Windows中為cmd.exe, Shell應當能識别-c開關在UNIX中,或/s /c在 Windows中。 -在Windows中,指令行解析應當能相容cmd.exe)
-timeout,數字,逾時時間(預設: 0)
-maxBuffer,數字, 在stdout或stderr中允許存在的最大緩沖(二進制),如果超出那麼子程序将會被殺死(預設: 200*1024)
-killSignal,字元串,結束信号(預設:'SIGTERM')
-uid,數字,設定使用者程序的ID
-gid,數字,設定程序組的ID
-callback :回調函數,包含三個參數error, stdout和stderr。
-exec()方法傳回最大的緩沖區,并等待程序結束,一次性傳回緩沖區的内容。
spawn() 方法
child_process.spawn使用指定的指令行參數建立新程序,文法格式如下:
child_process.spawn(command[, args][, options])
-command: 将要運作的指令
-args: Array字元串參數數組
-options Object
-cwd:String,子程序的目前工作目錄
-env:Object,環境變量鍵值對
-stdio:Array
String,子程序的stdio配置
-detached:Boolean,這個子程序将會變成程序組的上司
-uid:Number,設定使用者程序的ID
-gid:Number,設定程序組的ID
-spawn()方法傳回流 (stdout & stderr),在程序傳回大量資料時使用。程序開始執行spawn()時就開始接收響應。
fork 方法
child_process.fork是spawn()方法的特殊形式,用于建立程序,文法格式如下:
child_process.fork(modulePath[, args][, options])
-modulePath: String,将要在子程序中運作的子產品
-args: Array,字元串參數數組
-options:Object
-execPath:String,建立子程序的可執行檔案
-execArgv:Array,子程序的可執行檔案的字元串參數數組(預設: process.execArgv)
-silent:Boolean,如果為true,子程序的stdin,stdout和stderr将會被關聯至父程序,否則,它們将會從父程序中繼承。(預設為:false)
傳回的對象除了擁有ChildProcess執行個體的所有方法,還有一個内建的通信信道。
Node.js JXcore 打包
Node.js是一個開放源代碼、跨平台的、用于伺服器端和網絡應用的運作環境。
JXcore是一個支援多線程的 Node.js 發行版本,基本不需要對你現有的代碼做任何改動就可以直接線程安全地以多線程運作。
JXcore 安裝
下載下傳JXcore安裝包,然後進行解壓,在解壓的目錄下提供了jx二進制檔案指令,接下來我們主要使用這個指令。
步驟1、下載下傳
在http://jxcore.com/downloads/中下載下傳JXcore安裝包,你需要根據你自己的系統環境來下載下傳安裝包:
1、Window系統下載下傳:Download,
2、Linux/OSX下載下傳安裝指令,直接下載下傳解壓包下的jx二進制檔案,然後拷貝到/usr/bin目錄下:
$ wget https://s3.amazonaws.com/nodejx/jx_rh64.zip
$ unzip jx_rh64.zip
$ cp jx_rh64/jx /usr/bin
将/usr/bin添加到PATH路徑中:
$ export PATH=$PATH:/usr/bin
以上步驟如果操作正确,使用以下指令,會輸出版本号資訊:
$ jx --version
v0.10.32
包代碼
例如,我們的Node.js項目包含以下幾個檔案,其中index.js是主檔案:
drwxr-xr-x 2 root root 4096 Nov 13 12:42 images
-rwxr-xr-x 1 root root 30457 Mar 6 12:19 index.htm
-rwxr-xr-x 1 root root 30452 Mar 1 12:54 index.js
drwxr-xr-x 23 root root 4096 Jan 15 03:48 node_modules
drwxr-xr-x 2 root root 4096 Mar 21 06:10 scripts
drwxr-xr-x 2 root root 4096 Feb 15 11:56 style
接下來我們使用jx指令打包以上項目,并指定index.js為Node.js項目的主檔案:
$ jx package index.js index
以上指令執行成功,會生成以下兩個檔案:
-index.jxp:這是一個中間件檔案,包含了需要編譯的完整項目資訊。
-index.jx:這是一個完整包資訊的二進制檔案,可運作在用戶端上。