隨着 Web 2.0 概念和 Ajax 技術的流行,JavaScript 做爲 Ajax 應用開發中必不可少的一部分,已經獲得了普遍的流行。開發人員也開始逐步的熟悉和掌握 JavaScript,並積累了相關的開發經驗。雖然 JavaScript 目前主要用在 Web 應用中,以瀏覽器做爲運行平臺,可是已經有相關的嘗試把 JavaScript 遷移到服務器端,這其中包括 Aptana 的 Jaxer 等。這種作法與 Google GWT 是殊途同歸的。Google GWT 容許開發人員使用 Java 語言來編寫 Web 前端代碼。這兩種作法的目的都是爲了複用開發人員已經掌握的知識和積累的經驗。在這點上,node.js 相似於 Jaxer。html
簡單的來講,node.js 是一個容許開發人員使用 JavaScript 語言編寫服務器端代碼的框架。也就是說編寫的 JavaScript 代碼能夠直接運行在本地機器上,而不只限於瀏覽器。從實現的角度來講,Jaxer 和 node.js 都使用了已有的 JavaScript 執行引擎。Jaxer 用的是 Mozilla Firefox 中使用的 JavaScript 引擎,而 node.js 用的則是 Google Chrome 中用的 V8 引擎。前端
node.js 入門node
node.js 能夠運行在 Linux、Windows 和 Macintosh 等主流的操做系統上。在 Windows 平臺上運行 node.js 的話,須要 Cygwin 或是 MinGW 的支持。下面以經常使用的 Windows 平臺爲例來講明。首先須要安裝 Cygwin。安裝的時候須要選擇 gcc-g++
、make
、openssl
和 python
等包。gcc
的版本必須是最新的。接着從 參考資料 中給出的地址下載 node.js 0.4.0 版本的源代碼。下載解壓以後,依次在 Cygwin 中運行 ./configure
、make
和 make install
等命令進行編譯和安裝。安裝完成以後,直接運行 node
命令就能夠啓動 node.js 提供的命令行。在命令行中能夠直接輸入 JavaScript 代碼並運行。也能夠經過 node server.js
的方式來運行一個 JavaScript 文件 server.js
。python
代碼清單 1 中給出了一個簡單的「Hello World」程序的示例。經過 node helloworld.js
來運行該 JavaScript 文件以後,會在控制檯輸出「Hello World」。web
process.stdout.write("Hello World"); |
代碼清單 1 中的 process
表示的是當前運行的 node.js 進程,其屬性 stdout
表示的是進程的標準輸出流。經過 write()
方法向給流中寫入一個字符串。從 代碼清單 1 能夠看到,使用 JavaScript 就能夠訪問標準輸出流等本地系統上的資源。這從一個側面反映出來了 node.js 的強大。express
在 node.js 能夠運行的 JavaScript 代碼中,可使用一些全局的對象:包括 代碼清單 1 中用到的 process
、下面會介紹的用來加載模塊的 require()
方法、表示當前正在執行的 JavaScript 文件名的 __filename
、表示當前正在執行的 JavaScript 文件的目錄的 __dirname
和與瀏覽器中類似的用來執行定時任務的 setTimeout()
和 setInterval()
方法等。編程
在介紹了 node.js 的基本知識以後,下面介紹 node.js 的模塊化結構。json
回頁首canvas
模塊化結構數組
node.js 使用了 CommonJS 定義的模塊系統。不一樣的功能組件被劃分紅不一樣的模塊。應用能夠根據本身的須要來選擇使用合適的模塊。每一個模塊都會暴露一些公共的方法或屬性。模塊使用者直接使用這些方法或屬性便可,不須要關係模塊內部的實現細節。除了系統預置的多個模塊以外,應用開發團隊也能夠利用這個機制來將應用拆分紅多個模塊,以提升代碼的可複用性。
在 node.js 中使用一個模塊的方式是很是簡單的。使用某個模塊以前須要首先聲明對它的依賴。在 JavaScript 代碼中能夠直接使用全局函數 require()
來加載一個模塊。如 require("http")
能夠加載系統預置的 http
模塊。而 require("./myModule.js")
用來加載與當前 JavaScript 文件同一目錄下的myModule.js
模塊。若是使用 require()
的路徑以「/」開頭的話,則認爲是模塊 JavaScript 文件在操做系統上的絕對路徑。若是不是這兩種狀況的話,node.js 就會嘗試在當前 JavaScript 文件的父目錄及其祖先目錄下的 node_modules
目錄下查找。好比目錄 /usr/home/my.js
中調用了require("other.js")
的話,node.js 會依次嘗試查找下列文件:/usr/home/node_modules/other.js
、/usr/node_modules/other.js
和/node_modules/other.js
。
require()
方法的返回值是該模塊所暴露出來的公開 JavaScript 對象,包含了可供使用的方法和屬性。代碼清單 2 給出了模塊的基本使用方式。
var greetings = require("./greetings.js"); var msg = greetings.sayHello("Alex", "zh_CN"); process.stdout.write(msg); |
如 代碼清單 2 所示,通常是直接把 require()
方法的返回值賦值給一個變量,在 JavaScript 代碼中直接使用此變量便可。greetings.js
模塊暴露了一個sayHello()
方法,當前 JavaScript 代碼直接使用了該方法。
開發本身的模塊的基本工做是在模塊對應的 JavaScript 文件中編寫模塊相關的代碼。這其中封裝了模塊的內部處理邏輯。通常來講,一個模塊一般會暴露一些公開的方法或屬性給其使用者。模塊的內部代碼須要把這些方法或屬性給暴露出來。代碼清單 3 給出了 代碼清單 2 中所使用的 greetings.js
文件的內容。
var languages = { "zh_CN" : "你好,", "en" : "Hello, " }; exports.sayHello = function(name, language) { return languages[language] || languages["en"] + name; }; |
如 代碼清單 3 所示,exports
對象的內容就是模塊的使用者調用 require()
方法的返回值中所包含的內容。模塊經過這種方式來聲明其所暴露出來的公開方法和屬性。在模塊中定義的變量,如 languages
,是隻對模塊內部的代碼可見的。
若是一個模塊所包含的內容比較多,也能夠用文件夾的方式來組織。能夠在文件夾的根目錄下面建立一個 package.json
文件,其內容中包含了模塊的名稱和入口 JavaScript 文件的路徑。若是沒有提供這個 package.json
文件的話,node.js 會默認在文件夾中查找 index.js
文件做爲模塊的啓動 JavaScript 文件。
在介紹完 node.js 的模塊化結構以後,下面介紹其事件驅動機制。
開發過 Web 應用的人都熟悉瀏覽器中的事件處理機制。當對某個 DOM 元素上的某類事件感興趣的時候,只須要在該 DOM 元素上面註冊一個事件監聽器便可。如 ele.addEventListener("click", function() {})
就添加了一個對 click
事件的監聽器。當事件發生的時候,事件監聽器的 JavaScript 方法就會被調用。事件的處理方法是異步執行的。這種異步執行的方式很是適合於開發高性能併發網絡應用。實際上,目前的高性能併發應用開發通常有兩種作法:第一種是使用多線程的機制,另一種就是採用基於事件驅動的方式。多線程的問題在於應用開發起來難度較高,很容易出現線程飢餓或是死鎖等問題,對開發人員提出了更高的要求。而事件驅動的方式則更加靈活,很容易爲 Web 開發人員所理解和使用,也不存在線程死鎖等問題。依託於性能強大的 Google V8 引擎和先進的事件 I/O 架構,node.js 能夠成爲建立高性能服務器端應用的良好基礎。
基於 node.js 開發應用與開發 Web 應用有類似的編程模型。不少模塊都會暴露出一些事件,使用這些模塊的代碼經過註冊事件監聽器的方式來添加相應的處理邏輯。代碼清單 4 中給出了一個簡單的 HTTP 代理服務器的實現代碼。
var http = require("http"); var url = require("url"); http.createServer(function (req, res) { var urlObj = url.parse(req.url, true); // 獲取被代理的 URL var urlToProxy = urlObj.query.url; if (!urlToProxy) { res.statusCode = 400; res.end("URL 是必須的。"); } else { console.log("處理代理請求:" + urlToProxy); var parsedUrl = url.parse(urlToProxy); var opt = { host : parsedUrl.hostname, port : parsedUrl.port || 80, path : (parsedUrl.pathname || "") + (parsedUrl.search || "") + (parsedUrl.hash || "") }; http.get(opt, function(pres) { // 請求被代理 URL 的內容 res.statusCode = pres.statusCode; var headers = pres.headers; for (var key in headers) { res.setHeader(key, headers[key]); } pres.on("data", function(chunk) { res.write(chunk); // 寫回數據 }); pres.on("end", function() { res.end(); }); }); } }).listen(8088, "127.0.0.1"); console.log("代理服務器已經在 8088 端口啓動。"); |
整個代理服務器的實現比較簡單。首先經過 http
模塊中的 createServer()
方法用來建立一個 HTTP 服務器,再經過 listen()
方法就可讓該 HTTP 服務器在特定端口監聽。在 createServer()
方法中傳入的參數是 HTTP 請求的響應方法。實際上,每一個 HTTP 請求都是對應於 HTTP 服務器上的一個request
事件。代碼清單 4 中的 HTTP 服務器建立部分實際上等價於 代碼清單 5 中給出的實現方式。
var server = http.createServer(); server.on("request", function(req, res) { }); |
在請求的處理方法裏面,經過 http.get()
方法來獲取被代理 URL 的內容。這裏一樣採用了基於事件的處理方式。pres.on("data", function(chunk) {})
在 pres
的 data
事件上添加了一個處理方法。該方法的做用是當獲取到被代理 URL 的內容的時候,就把獲取到的內容寫回到原始 HTTP 請求的響應中。對於 end
事件的處理也是一樣的。在使用 node.js 進行開發的時候,會常常遇到這種使用事件處理方法和回調方法的場景。
在介紹了 node.js 的事件驅動機制以後,下面介紹一些經常使用的模塊。
node.js 默認提供了不少與網絡與文件系統操做相關的模塊。這些模塊是構建服務器端應用的基礎。下面對其中一些常見的模塊進行具體說明。
前面提到過,node.js 採用的是事件驅動的架構,其中的不少模塊都會產生各類不一樣的事件,能夠由模塊使用者來添加事件處理方法。全部可以產生事件的對象都是事件模塊中的 EventEmitter
類的實例。
EventEmitter
類中的方法都與事件的產生和處理相關,以下所示:
addListener(event, listener)
和 on(event, listener)
:這兩個方法的做用都是用來爲某個事件 event
添加事件處理方法 listener
。once(event, listener)
:這個方法爲某個事件 event
添加僅執行一次的處理方法 listener
。處理方法在執行一次以後就會被刪除。removeListener(event, listener)
:該方法用來刪除某個事件 event
上的處理方法 listener
。emit(event, [arg1], [arg2], [...])
:該方法用來產生某個事件 event
。事件名稱 event
以後的參數被傳遞給對應的事件處理方法。代碼清單 6 給出了事件模塊的使用示例。
var events = require("events"); var emitter = new events.EventEmitter(); emitter.on("myEvent", function(msg) { console.log(msg); }); emitter.emit("myEvent", "Hello World."); |
在事件模塊中有一個特殊的事件 error
。當出現錯誤的時候,EventEmitter
會產生此事件。若是此事件沒有對應的處理方法的話,默認的行爲是輸出錯誤信息後,程序自動終止。所以,須要注意老是添加一個對 error
事件的處理方法。
node.js 中存在各式各樣不一樣的數據流,包括文件系統、HTTP 請求和響應、以及 TCP/UDP 鏈接等。這些流都是 EventEmitter
的實例,所以能夠產生各類不一樣的事件。流能夠分紅只讀、只寫和讀寫流三種。
可讀流主要會產生 4 個事件:
data
:當讀取到流中的數據時,產生此事件。end
:當流中沒有數據可讀時,產生此事件。error
:當讀取數據出現錯誤時,產生此事件。close
:當流被關閉時,產生此事件。 除了上面的事件以外,還有一個 pipe()
方法能夠用來把當前的可讀流和另一個可寫流鏈接起來。可讀流中的數據會被自動寫入到可寫流中。
可寫流中最經常使用的是 write()
和 end()
兩個方法。write()
方法用來向流中寫入數據,而 end()
則用來結束寫入操做。
爲了表示二進制數據,node.js 使用了類 Buffer
來表示數據緩衝區,以對二進制數據進行操做。Buffer
類內部是以數組的方式來存儲數據的。一旦建立出來以後,Buffer
的大小是不能被修改的。Buffer
類的實例是能夠與 JavaScript 中的字符串類型互相轉換的。在轉換的時候須要指定編碼格式。經過Buffer
類的 toString(encoding, start, end)
方法能夠把 Buffer
中的從 start
到 end
的內容轉換成以 encoding
編碼的字符串。能夠支持的編碼格式有:ascii
、utf8
和 base64
。經過 new Buffer(str, encoding)
能夠用一個字符串 str
來初始化一個緩衝區。write(string, offset, encoding)
用來把一個字符串 string
以編碼格式 encoding
寫入到緩衝區中以 offset
開始的位置上。
node.js 提供了一些與網絡操做相關的模塊,包括 TCP、UDP 和 HTTP 等,能夠實現網絡服務器和客戶端。
與 TCP 協議相關的實如今 net
模塊中。經過該模塊的 createServer(connectionListener)
方法能夠建立一個 TCP 服務器。參數 connectionListener
是當有客戶端鏈接到該服務器上時的處理方法,等價於對 connect
事件的處理。一個 TCP 服務器是類 Server
的實例。經過 listen
方法可讓服務器在指定端口監聽。
若是想鏈接到一個已有的 TCP 服務器的話,可使用 createConnection(port, host)
方法來鏈接到指定主機 host
的端口 port
上。該方法的返回值是Socket
類的實例,表示一個套接字鏈接。獲得一個 Socket
類的實例以後,就能夠經過 write()
方法來向該鏈接中寫入數據。若是想從該鏈接上獲取數據的話,能夠添加 data
事件的處理方法。
代碼清單 7 中給出了一個簡單的用來進行表達式計算的 TCP 服務器,能夠經過 telnet
命令鏈接到此服務器來進行測試。
var net = require("net"); var server = net.createServer(function(socket) { socket.setEncoding("utf8"); var buffer = [], len = 0; socket.on("data", function(data) { // 接收到客戶端數據 if (data.charCodeAt(0) == 13) { var expr = buffer.join(""); try { var result = eval(expr); // 進行計算 socket.write(result.toString()); // 寫回結果 } catch (e) { socket.write("Wrong expression."); } finally { socket.write("\r\n"); buffer = []; } } else { buffer.push(data); } }); }); server.listen(8180, "127.0.0.1"); console.log("服務器已經在端口 8180 啓動。"); |
除了 TCP 服務器外,模塊 http
和 https
能夠分別實現 HTTP 和 HTTPS 服務器,模塊 dgram
能夠實現 UDP/Datagram 套接字鏈接,模塊 tls
能夠實現安全的套接字鏈接(SSL)。這些模塊的使用方式都相似於模塊 tcp
。
node.js 中的 fs
模塊用來對本地文件系統進行操做。fs
模塊中提供的方法能夠用來執行基本的文件操做,包括讀、寫、重命名、建立和刪除目錄以及獲取文件元數據等。每一個操做文件的方法都有同步和異步兩個版本。異步操做的版本都會使用一個回調方法做爲最後一個參數。當操做完成的時候,該回調方法會被調用。而回調方法的第一個參數老是保留爲操做時可能出現的異常。若是操做正確成功,則第一個參數的值是 null
或 undefined
。而同步操做的版本的方法名稱則是在對應的異步方法以後加上一個 Sync
做爲後綴。好比異步的 rename()
方法的同步版本是 renameSync()
。下面列出來了 fs
模塊中的一些經常使用方法,都只介紹異步操做的版本。
rename(path1, path2)
:將路徑 path1
表示的目錄或文件重命名成路徑 path2
。truncate(fd, len)
:將文件描述符 fd
對應的文件的長度截斷爲 len
。chmod(path, mode)
:將路徑 path
表示的目錄或文件的權限修改成 mode
。stat(path)
:獲取路徑 path
表示的目錄或文件的元數據。元數據用 Stats
類來表示。open(path, flags, mode)
:打開一個路徑 path
表示的文件。回調方法中能夠獲得該文件的描述符。read(fd, buffer, offset, length, position)
:讀取給定文件描述符 fd
所表示的文件中從 position
位置開始的長度爲 length
字節的數據,並存放到緩衝區 buffer
中從 offset
起始的位置上。回調方法中能夠獲得實際讀取的字節數。write(fd, buffer, offset, length, position)
:將緩衝區 buffer
中的數據寫入到文件描述符 fd
所表示的文件中。參數的含義與 read()
方法同樣。回調方法中能夠獲得實際寫入的字節數。readFile(filename, encoding)
:以編碼格式 encoding
來讀取一個文件 filename
中的內容。回調方法中能夠獲得文件的內容。writeFile(filename, data, encoding)
:將數據 data
以編碼格式 encoding
寫入到文件 filename
中。 除了上面列出來的直接操做文件自己的方法外,還能夠把文件轉換成流。createReadStream(path, options)
和 createWriteStream(path, options)
分別用來從文件中建立可讀和可寫流。參數 path
表示的是文件的路徑,options
是一個表示讀取或寫入文件時的選項的 JavaScript 對象。
代碼清單 8 中給出了一個簡單的 HTTP 靜態文件服務器的實現。
var http = require("http"), fs = require("fs"), path = require("path"), url = require("url"); var server = http.createServer(function(req, res) { var pathname = url.parse(req.url).pathname; var filepath = path.join("/tmp", "wwwroot", pathname); var stream = fs.createReadStream(filepath, {flags : "r", encoding : null}); stream.on("error", function() { res.writeHead(404); res.end(); }); stream.pipe(res); }); server.on("error", function(error) { console.log(error); }); server.listen(8088, "127.0.0.1"); |
如 代碼清單 8 所示,首先把 HTTP 請求的路徑轉換成服務器上文件路徑,再從文件中建立可讀流,最後經過 pipe()
方法把文件的數據流傳遞到 HTTP 請求的響應中。
除了上面介紹的這些常見模塊以外,node.js 還提供了一些輔助的模塊。
模塊 path
用來處理文件系統上的路徑。這個模塊中的 join()
用來把多個路徑鏈接起來,造成一個完整的路徑。如 join("/usr", "home", "test/index.html")
的結果是路徑 /usr/home/test/index.html
。normalize()
用來對路徑進行歸一化操做,去掉其中多餘的「/」以及處理「..」和「.」。resolve([from ...], to)
方法用來獲取給定路徑 to
的絕對路徑。若是 to
不是絕對路徑,就把它以前的參數按從右到左的順序添加上去,直到獲得了一個絕對路徑。若是到最後仍是沒法獲得絕對路徑,就把當前的工做目錄加上。假設當前的工做目錄是 /usr/home
,那麼 resolve("test", "index.html")
的返回結果是 /usr/home/test/index.html
。dirname()
方法用來獲取路徑的目錄部分。如 dirname("/usr/home/index.html")
的返回結果是 /usr/home
。basename()
用來獲取路徑的最後一個部分。如 basename("/usr/home/index.html")
的返回結果是 index.html
。extname()
用來獲取一個路徑的文件擴展名部分。如 extname("/usr/home/index.html")
的返回結果是 .html
。
模塊 url
用來對 URL 進行解析。parse(urlStr, parseQueryString)
方法用來把一個 URL 字符串 urlStr
解析成主機名、端口和路徑等幾個部分。該方法的返回值是一個包含了 protocol
、hostname
、port
、pathname
和 query
等屬性的 JavaScript 對象。若是參數 parseQueryString
的值爲 true
的話,URL 中包含的查詢字符串部分也會被解析。format(urlObj)
方法的做用與 parse()
方法正好相反,用來從一個 JavaScript 對象中構建出 URL 字符串。
模塊 querystring
用來處理 URL 中的查詢字符串。stringify(obj)
方法用來把一個 JavaScript 對象 obj
轉換成查詢字符串的格式。如 stringify({a : 1, b : "good"})
的返回結果是 a=1&b=good
。parse(str)
用來把一個查詢字符串解析成 JavaScript 對象。
模塊 vm
能夠用來執行 JavaScript 代碼。方法 runInThisContext(code)
用來執行一段 JavaScript 代碼 code
並返回其結果。經過該方法運行的 JavaScript 代碼不能訪問當前代碼的做用域。runInNewContext(code, [sandbox])
方法也是用來執行 JavaScript 代碼的,與 runInThisContext()
不一樣的是經過該方法運行的 JavaScript 代碼使用 sandbox
對象做爲全局對象。如 runInNewContext("a + 3", {a : 4})
的返回結果是 7。createScript(code)
方法用來預先編譯一段 JavaScript 代碼,可是並不當即執行。該方法的返回值是一個 Script
對象。該對象一樣有 runInThisContext()
和runInNewContext([sandbox])
兩個方法,含義與上面提到的兩個方法相似。
模塊 os
提供了與底層操做系統相關的一些信息。包括 hostname()
用來獲取操做系統的主機名;type()
用來獲取操做系統的類型;release()
用來獲取操做系統的發行版本號;uptime()
用來獲取以秒計算的系統運行時間;cpus()
用來獲取 CPU 的相關信息。freemem()
和 totalmem()
分別用來獲取系統的內存總數和可用內存數。
模塊 util
提供了一些經常使用的輔助方法。debug(string)
方法用來輸出信息到標準錯誤流。log(string)
方法用來輸出附帶時間戳的信息到標準輸出流。inspect(object, showHidden, depth)
方法用來輸出一個對象的內部結構,參數 object
是要查看的對象,showHidden
表示是否查看對象的隱藏屬性,depth
表示查看的對象層次結構的深度,默認值是 2。inherits(constructor, superConstructor)
方法用來實現 JavaScript 中基於原型的繼承機制。
在介紹完 node.js 提供的經常使用模塊以後,下面經過一個完整的示例來講明 node.js 的用法。
這個實例實現的功能是動態監測服務器的內存使用狀態,即內存的佔用率。獲取服務器上的內存佔用率比較簡單,只須要使用 os
模塊提供的方法便可,即freemem()/totalmem()
。爲了可以實時的監測內存佔有率,服務器須要實時的把數據傳輸給瀏覽器端。這裏最好的實現方式是 HTML 5 中引入的 WebSocket 規範。該規範在 Firefox 4 和 Google Chrome 等新瀏覽器上獲得了支持。同時服務器端也須要支持此規範。Socket.IO 在 node.js 上提供了對 WebSocket 規範的支持,包括服務器端和瀏覽器端代碼。代碼清單 9 給出了使用 Socket.IO 的服務器端代碼。
var io = require('./socket.io'); var io = io.listen(server); io.on("connection", function(client){ setInterval(function() { client.send(os.freemem() / os.totalmem()); }, 500); }); |
在 代碼清單 9 中,server
是 node.js 中的一個 HTTP 服務器對象,用來響應通常的 HTTP 請求。Socket.IO 能夠對 node.js 的 HTTP 服務器的請求進行攔截,將部分請求交給 Socket.IO 來處理。這裏的處理邏輯是當有客戶端鏈接上的時候,就每隔 500 毫秒把服務器的內存佔用率發送給客戶端。代碼清單 10 給出了瀏覽器端的 HTML 和 JavaScript 代碼。
<!doctype html> <html> <head> <title> 服務器內存使用狀況 </title> <script src="/socket.io/socket.io.js"></script> <style> #usage {border : 1px dashed green;} </style> <script> var canvas, width = 200, height = 200, buffer = [], max = 200; function updateChart(data) { if (buffer.length >= max) { buffer.unshift(); } buffer.push(data); var ctx = canvas.getContext("2d"); ctx.clearRect(0, 0, width, height); for (var i = 1, n = buffer.length; i < n; i++) { ctx.strokeStyle = "red"; ctx.beginPath(); ctx.moveTo(i - 1 , buffer[i - 1] * height); ctx.lineTo(i, buffer[i] * height); ctx.closePath(); ctx.stroke(); } } function onLoad() { canvas = document.getElementById("usage"); var socket = new io.Socket(null, {port: 8088}); socket.connect(); // 鏈接到服務器 socket.on("message", function(obj){ // 接收到消息時的處理方法 updateChart(obj); }); } </script> </head> <body onload="onLoad();"> <h1> 內存使用狀況 </h1> <canvas id="usage" width="200" height="200"></canvas> </body> </html> |
如 代碼清單 10 所示,首先創建一個與服務器之間的 WebSocket 鏈接。經過 message
事件定義了當接收到服務器端的消息時,更新瀏覽器端的顯示。瀏覽器端經過一個 HTML 5 提供的 canvas 來繪製內存佔用率的曲線圖,如 圖 1 所示。
一提到服務器端開發,開發人員通常想到的就是 Java 和 C/C++ 等語言。可是經過 node.js 提供的強大能力,熟悉 JavaScript 的 Web 開發人員也能夠開發出服務器端的應用。本文詳細介紹了 node.js 的事件驅動機制和模塊化結構,並對其中的經常使用模塊作了詳細說明,最後經過一個完整的實例展現了 node.js 的實用性。