Node.js是一個服務器端的開發框架,它基於Google Chrome的V8 JavaScript引擎構建。盡管Node.js自身是使用C++開發的,但是它使用JavaScript作為其應用語言。
Node.js有四個概念對於初學者非常重要,應該理解並掌握它們。如下:
一、非阻塞或異步I/O
由於Node.js是一個服務器端框架,因此它的主要工作之一就是處理來自浏覽器的請求。在傳統的I/O系統中,只有先前請求的響應返回來後,新的請求才能發出。這也就是為什麼稱之為阻塞I/O的通信。服務器阻塞了下一個到來的請求,然後處理當前的請求,直到請求處理完成,發出響應,再解除下一個到來的請求的阻塞。
Node.js不遵循上面的阻塞I/O通信原則。如果一個請求需要的處理時間較長,Node.js會把請求發送到事件循環中,然後在調用棧上處理下一個請求。一旦事件循環中的請求完成了處理,它會通知Node.js,Node.js會返回響應給浏覽器。下面可以看一個例子:
1、阻塞式I/O
// 餐桌1,獲取訂單1
var order1 = orderBlocking(['Coke', 'Iced Tea']);
// 服務訂單1
serveOrder(order1);
// 一旦訂單服務完成,服務員去另一張餐桌
// 餐桌2,訂單2
var order2 = orderBlocking(['Coke', 'Water']);
// 服務訂單2
serveOrder(order2);
// 一旦訂單服務完成,服務員去另一張餐桌
// 餐桌3,訂單3
var order3 = orderBlocking(['Iced Tea', 'Water']);
// 服務訂單3
serveOrder(order3);
// 一旦訂單服務完成,服務員去另一張餐桌
上面的例子中,服務員在第一個餐桌獲得訂單,然後向訂單提供服務,服務完成後,服務員立刻移動到下一張餐桌獲得訂單。訂單是按時間順序進行處理的,服務器僅僅是服務於訂單和阻塞其它的訂單。
2、非阻塞式I/O
// 在餐桌1取走交付的訂單並移動到下一個餐桌
orderNonBlocking(['Coke', 'Iced Tea'], function(drinks){
return serveOrder(drinks);
});
// 在餐桌2取走交付的訂單並移動到下一個餐桌
orderNonBlocking(['Beer', 'Whiskey'], function(drinks){
return serveOrder(drinks);
});
//在餐桌3取走交付的訂單並移動到下一個餐桌
orderNonBlocking(['Hamburger', 'Pizza'], function(food){
return serveOrder(food);
});
在上面的例子中,服務員去獲取訂單並通知廚師,再去下一個餐桌。在第一個訂單被處理期間,服務員移動到下一個餐桌去獲取訂單,服務員不阻塞訂單。
二、原型
在JavaScript中,原型即Prototype,是一個比較復雜的概念。Node.js使用了原型的地方很多,因此每一個JavaScript開發者都應該熟悉這個概念。
像Java、C++等編程語言都實現了典型的繼承,這有助於代碼的重用。首先構建一個基類(作為對象的藍圖),然後從這個類創建對象或擴展這個類。
但是JavaScript語言沒有這樣的概念。首先在JavaScript中創建一個對象,然後擴展這個對象或者從這個對象中創建新的對象。這就是所謂的原型繼承,它通過原型來實現。
每一個JavaScript對象都鏈接到一個原型對象,並且可以從原型對象中繼承其屬性。原型有點類似於面向對象語言中的類,但實際上是不同的,它們自身都是對象。每一個對象都鏈接到Object.prototype,它是JavaScript預定義的對象。
如果你在通過obj.propName或obj[‘propName’]來查看屬性時,這個對象有沒有這樣的屬性,可以通過obj.hasOwnProperty(‘propName’)來檢查,JavaScript的運行時會查看原型對象中是否有這個屬性。如果原型對象沒有這樣的屬性,然後再依次檢查此對象本身有沒有這樣的屬性(有可能對象繼承了幾級),直到匹配到此屬性。如果整個屬性鏈都沒有這樣的屬性,那麼就會返回未定義的值。
用例子來說明這一點:
if (typeof Object.create !== 'function') {
Object.create = function (o) {
var F = function () {};
F.prototype = o;
return new F();
};
var otherPerson = Object.create(person);
當創建了一個新對象時,你可以選擇這個對象的原型。在上面的代碼中,我們增加了一個Object函數的create方法。create方法創建了一個新對象,並且使用了另一個對象作為它的原型,並作為參數傳遞到新對象。
當我們修改了新對象,它的原型還保留原樣,不受影響。但是如果我們要修改原型對象,那麼就會影響到所有基於此原型對象的對象。
原型是一個很復雜的概念,需要繼續深入。
三、模塊
如果你熟悉Java語言的包Package概念,那麼你會理解Node.js的模塊這個概念,兩者沒什麼不同。模塊是簡單的JavaScript文件,它包含了特定目的的代碼。模塊模式(Module Pattern)用於簡化代碼的組織和導航。要使用模塊的屬性,你必須在JavaScript中require導入它,與Java語言中的import導入相同。
Node.js中有兩者類型的模塊:
1、核心模塊(Core Module)
核心模塊包含了預編譯到Node.js的庫。核心模塊的模板是向開發者提供經常出現和重復的代碼片段,如果沒有這些,那麼開發者會陷入這些大量重復且冗長無趣的工作中。常見的核心模塊包括:HTTP模塊、URL模塊、EVENTS模塊、文件系統模塊等。
2、用戶定義的模塊(User Defined Module)
用戶定義的模塊是開發者為了特定功能自己實現的模塊。通常是核心模塊不能滿足所需的功能時開發的。用戶定義的模塊同樣需要require導入。如果是核心模塊,require導入只需傳入模塊名,而對於用戶定義的模塊,require導入還需要傳入文件系統的路徑。
比如:
// 導入核心模塊
var http = require('http);
// 導入用戶定義的模塊
var something = require('./folder1/folder2/folder3/something.js');
四、回調
在JavaScript語言中,函數被認為是第一級的對象。這意味著開發者可以把函數當作是常規對象那樣,做所有的操作。可以把函數賦值給一個變量,還可以把函數作為參數傳遞給方法,還可以把函數作為對象的屬性,甚至可以從函數返回函數。
回調是JavaScript語言中的一種匿名函數,它可以作為參數傳遞給另一個函數,還可以在隨後的函數執行中執行回調函數或返回回調函數。這是回調函數廣泛使用的編程范式。
把回調函數作為參數傳遞給另一個函數時,我們只需傳遞函數定義,也即,我們無需知道回調函數什麼時候得以執行。這完全取決於調用函數的機制,故以回調命名。回調函數是Node.js的非阻塞通信和異步處理的基礎。
setTimeout(function() {
console.log("world");
}, 2000)
console.log("hello");
這是最簡單的回調函數的例子之一,我們把匿名函數當作參數傳遞給setTimeout函數,而匿名函數僅僅是在控制台輸出日志“world”。由於這只是函數定義,我們並不知道它什麼時候得以執行,執行取決於setTimeout函數的2000毫秒後輸出。
故第二個日志語句先在控制台輸出“hello”,然後等兩秒後再輸出回調函數定義的日志“world”。
// output
hello
world
理解以上四個概念有助於深入Node.js。
下面關於Node.js的內容你可能也喜歡:
在 Ubuntu 14.04/15.04 上安裝配置 Node.js v4.0.0 http://www.linuxidc.com/Linux/2015-10/123951.htm
如何在CentOS 7安裝Node.js http://www.linuxidc.com/Linux/2015-02/113554.htm
Ubuntu 14.04下搭建Node.js開發環境 http://www.linuxidc.com/Linux/2014-12/110983.htm
Ubunru 12.04 下Node.js開發環境的安裝配置 http://www.linuxidc.com/Linux/2014-05/101418.htm
Node.Js入門[PDF+相關代碼] http://www.linuxidc.com/Linux/2013-06/85462.htm
Node.js開發指南 高清PDF中文版 +源碼 http://www.linuxidc.com/Linux/2014-09/106494.htm
Node.js入門開發指南中文版 http://www.linuxidc.com/Linux/2012-11/73363.htm
Ubuntu 編譯安裝Node.js http://www.linuxidc.com/Linux/2013-10/91321.htm
Node.js 的詳細介紹:請點這裡
Node.js 的下載地址:請點這裡