(四)NodeJs核心模塊

本學習筆記是根據《Node.js開發指南》一書進行學習。

全局對象

JavaScript中有一個特殊的對象,稱為全局對象(Global Object),它及其所有屬性都可以在程序的任何地方訪問,即全局變量。在瀏覽器JavaScript中,通常window是全局對象,而NodeJs中的全局對象是global,所有全局變量(除了global本身以外)都是global對象的屬性。

全局對象和全局變量

按照ECMAScript的定義,滿足以下條件的變量是全局變量:

  • 在最外層定義的變量
  • 全局對象的屬性
  • 隱式定義的變量(未定義直接賦值的變量)

在NodeJs中你不可能在最外層定義變量,因為所有用戶代碼都是屬于當前模塊的,而模塊本身不是最外層上下文。

永遠使用var定義變量以避免引入全局變量,因為全局變量會污染命名空間,提高代碼的耦合風險。

process

process是一個全局變量,即global對象的屬性。它用于描述當前NodeJs進程狀態的對象,提供了一個與操作系統的簡單接口。

  • process.argv是命令行參數數組,第一個元素是node,第二個元素是腳本文件名,從第三個元素開始每個元素是一個運行參數。
  • process.stdout是標準輸出流,通常我們使用的console.log()向標準輸出打印字符,而process.stdout.write()函數提供了更底層的接口。
    -process.stdin是標準輸入流,初始時它是被暫停的,要想從標準輸入讀取數據,你必須恢復流,并手動編寫流的事件響應函數。
  • process.nextTick(callback)的功能是為事件循環設置一項任務,NodeJs會在下次事件循環調響應時調用callback
// debug.js
console.log(process.argv);

process.stdin.resume();
process.stdin.on("data", function (data) {
    process.stdout.write(data.toString());
});

結果:

image.png

不要使用setTimeout(fn, 0)代替process.nextTick(callback),前者比后者效率要低得多。

console

console對象用于向標準輸出流(stdout)或標準錯誤流(stderr)輸出字符。

  • console.log()向標準輸出流打印字符并以換行符結束。
  • console.error():與console.log()用法相同,只是向標準錯誤流輸出。
  • console.trace():向標準錯誤流輸出當前的調用棧。

console.log接受若干個參數,如果只有一個參數,則輸出這個參數的字符串形式。如果有多個參數,則以類似于C語言printf()命令的格式輸出。第一個參數是一個字符串,如果沒有參數,只打印一個換行。

常用工具util

util是一個Node.js核心模塊,提供常用函數的集合

util.inherits

JavaScript的面向對象特性是基于原型的,與常見的基于類的不同。JavaScript沒有提供對象繼承的語言級別特性,而是通過原型復制來實現的

util.inherits(constructor, superConstructor)是一個實現對象間原型繼承的函數。

// inherits.js
var util = require("util");

function Base() {
    this.name = "base";
    this.base = 1991;
    this.sayHello = function () {
        console.log("Hello"+this.name);
    };
}
Base.prototype.showName = function () {
    console.log(this.name);
};

function Sub() {
    this.name = "sub";
}

util.inherits(Sub, Base);

var newBase = new Base();
newBase.showName();
newBase.sayHello();
console.log(newBase);

var newSub = new Sub();
newSub.showName();
// newSub.sayHello();
console.log(newSub);
image.png

Sub僅僅繼承了Base在原型中定義的函數,而構造函數內部創造的base屬性和sayHello函數都沒有被Sub繼承。同時,在原型中定義的屬性不會被console.log作為對象的屬性輸出。

util.inspect

util.inspect(object,[showHidden],[depth],[colors])是一個將任意對象轉換為字符串的方法,通常用于調試和錯誤輸出。它至少接受一個參數object,即要轉換的對象。

util還提供了util.isArray()util.isRegExp()util.isDate()util.isError()四個類型測試工具,以及util.format()util.debug()等工具。

事件驅動events

events是NodeJs最重要的模塊。NodeJs本身架構就是事件式的,而它提供了唯一的接口,所以堪稱NodeJs事件編程的基石。

事件發射器

events模塊只提供了一個對象:events.EventEmitterEventEmitter的核心就是事件發射與事件監聽器功能的封裝EventEmitter每個事件由一個事件名和若干個參數組成事件名是一個字符串,通常表達一定的語義。對于每個事件,EventEmitter支持若干個事件監聽器。當事件發射時,注冊到這個事件的事件監聽器被依次調用,事件參數作為回調函數參數傳遞。

var EventEmitter = require("events").EventEmitter;
var event = new EventEmitter();
event.on("some_event", function(){
    console.log("some_event start1");
});
event.on("some_event", function () {
    console.log("some_event start2")
});

event.emit("some_event");

執行以上代碼輸出:

some_event start1
some_event start2

運行結果中可以看到兩個事件監聽器回調函數被先后調用。

EventEmitter常用的API:

  • EventEmitter.on(event, listener)為指定事件注冊一個監聽器,接受一個字符串event和一個回調函數listener
  • EventEmitter.emit(event, [arg1], [arg2], [...])發射event事件,傳遞若干可選參數到事件監聽器的參數表。
  • EventEmitter.once(event, listener)為指定事件注冊一個單次監聽器,即監聽器最多只會觸發一次,觸發后立刻解除該監聽
  • EventEmitter.removeListener(event, listener)移除指定事件的某個監聽器,listener必須是該事件已經注冊過的監聽器。
  • EventEmitter.removeAllListeners([event])移除所有事件的所有監聽器,如果指定event,則移除指定事件的所有監聽器。
var EventEmitter = require("events").EventEmitter;
var event = new EventEmitter();
event.on("some_event", function(){
    console.log("some_event start1");
});
event.on("some_event", function () {
    console.log("some_event start2")
});

event.emit("some_event");

setTimeout(function(){
    event.emit("some_event");
}, 1000);

event.removeAllListeners("some_event");
event.emit("some_event");

執行以上代碼,輸出:

image.png

error事件

EventEmitter定義了一個特殊的事件error,它包含了“錯誤”的語義,我們在遇到異常的時候通常會發射error事件。

繼承EventEmitter

大多數時候我們不會直接使用EventEmitter,而是在對象中繼承它。包括fsnethttp在內的,只要是支持事件響應的核心模塊都是EventEmitter的子類。

原因有兩點。首先,具有某個實體功能的對象實現事件符合語義,事件的監聽和發射應該是一個對象的方法。其次JavaScript的對象機制是基于原型的,支持部分多重繼承,繼承EventEmitter不會打亂對象原有的繼承關系。

文件系統fs

fs.readFile

fs.readFile(filename, [encoding], [callback(err, data)])是最簡單的讀取文件的函數。

var fs = require("fs");
fs.readFile("server.js", "utf-8", function(err, data){
    if (err){
        console.log(err);
    }else{
        console.log(data);
    }
})

fs.readFileSync

fs.readFileSync(filename, [encoding])fs.readFile同步的版本。它接受的參數和fs.readFile相同,而讀取到的文件內容會以函數返回值的形式返回。如果有錯誤發生,fs將會拋出異常,你需要使用trycatch捕捉并處理異常。

fs.open

fs.read

一般來說,除非必要,否則不要使用以上兩種方式讀取文件,因為它要求你手動管理緩沖區和文件指針,尤其是在你不知道文件大小的時候,這將會是一件很麻煩的事情。

HTTP服務器與客戶端

HTTP服務器

http.Server的事件

http.Server是一個基于事件的HTTP服務器,所有的請求都被封裝為獨立的事件,開發者只需要對它的事件編寫響應函數即可實現HTTP服務器的所有功能。它繼承自EventEmitter,提供了以下幾個事件:

  • request:當客戶端請求到來時,該事件被觸發,提供兩個參數reqres,分別是http.ServerRequesthttp.ServerResponse的實例,表示請求和響應信息。
  • connection:當TCP連接建立時,該事件被觸發,提供一個參數socket,為net.Socket的實例。connection事件的粒度要大于request,因為客戶端在Keep-Alive模式下可能會在同一個連接內發送多次請求。
  • close :當服務器關閉時,該事件被觸發。注意不是在用戶連接斷開時。
  • checkContinue、upgrade、clientError事件。

最常用的就是request了,因此http提供了一個捷徑:http.createServer([requestListener]),功能是創建一個HTTP服務器并將requestListener作為request事件的監聽函數。

http.ServerRequest

一般由http.Serverrequest事件發送,作為第一個參數傳遞,通常簡稱request或req。http.ServerRequest提供了以下3個事件用于控制請求體傳輸:

  • data :當請求體數據到來時,該事件被觸發。該事件提供一個參數chunk,表示接收到的數據。如果該事件沒有被監聽,那么請求體將會被拋棄。該事件可能會被調用多次。
  • end :當請求體數據傳輸完成時,該事件被觸發,此后將不會再有數據到來。
  • close:用戶當前請求結束時,該事件被觸發。不同于end,如果用戶強制終止了傳輸,也還是調用close。

獲取GET請求內容

url模塊中的parse函數提供解析客戶端的表單請求。

// httpServerRequestGet.js
var http = require("http");
var url = require("url");
var util = require("util");

http.createServer(function(req, res){
    res.writeHead(200, {"Conetnet-Type": "text/html"});
    res.end(util.inspect(url.parse(req.url, true)));
}).listen(3000);

在瀏覽器中訪問http://127.0.0.1:3000/user?name=byvoid&email=byvoid@byvoid.com

image.jpg

通過url.parse,原始的path被解析為一個對象,其中query就是我們所謂的GET請求的內容,而路徑則是pathname

獲取POST請求內容

// httpServerRequestPost.js
var http = require("http");
var querystring = require("querystring");
var util = require("util");

http.createServer(function(req, res){
    var post = "";
    req.on("data", function(chunk){
        post += chunk
    });
    req.on("end", function(){
        post = querystring.parse(post);
        res.end(util.inspect(post));
    });
}).listen(3000)

通過事件監聽函數。上面的代碼僅供理解使用,在實際編碼中不贊同這樣的做法。

http.ServerResponse

返回給客戶端的信息,也是由http.Server的request事件發送的,作為第二個參數傳遞,一般簡稱為response或res。

http.ServerResponse有三個重要的成員函數,用于返回響應頭、響應內容以及結束請求。

  • response.writeHead(statusCode, [headers]):該函數在一個請求內最多只能調用一次。
  • response.write(data, [encoding]):在response.end調用之前,response.write可以被多次調用。
  • response.end([data], [encoding]): 結束響應,告知客戶端所有發送已經完成。

HTTP客戶端

http模塊提供了兩個函數http.requesthttp.get

  • http.request(options,callback)發起HTTP請求。接受兩個參數,option是一個類似關聯數組的對象,表示請求的參數,callback是請求的回調函數。
// httpRequst.js
var http = require("http");
var querystring = require("querystring");

var content = querystring.stringify({
    name: "byvoid",
    email: "byvoid@byvoid.com",
    address: "Tangshan",
});

var options = {
    host: "127.0.0.1",
    port: 3000,
    path: '/user?hello=wen',
    method: "POST",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Content-Length": content.length
    }
};

var req = http.request(options, function(res){
    res.setEncoding("utf-8");
    res.on("data", function(data){
        console.log(data);
    });
});

req.write(content);
req.end();
  • http.get(options, callback) http模塊還提供了一個更加簡便的方法用于處理GET請求:http.get。它是http.request的簡化版,唯一的區別在于http.get自動將請求方法設為了GET請求,同時不需要手動調用req.end()

http.ClientRequest

http.ClientRequest是由http.requesthttp.get返回產生的對象,表示一個已經產生而且正在進行中的HTTP請求

http.ClientResponse

提供了三個事件dataendclose,分別在數據到達、傳輸結束和連接結束時觸發,其中data事件傳遞一個參數chunk,表示接收到的數據。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,533評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,055評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,365評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,561評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,346評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,889評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,978評論 3 439
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,118評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,637評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,558評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,739評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,246評論 5 355
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,980評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,362評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,619評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,347評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,702評論 2 370

推薦閱讀更多精彩內容

  • https://nodejs.org/api/documentation.html 工具模塊 Assert 測試 ...
    KeKeMars閱讀 6,356評論 0 6
  • 內容來自《Node.js開發指南》 核心模塊是 Node.js 的心臟,它由一些精簡而高效的庫組成,為 Node....
    angelwgh閱讀 907評論 0 1
  • Module definition patterns 除了作為加載依賴的機制之外,模塊系統也是一種用于定義AP...
    宮若石閱讀 481評論 0 0
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,776評論 18 139
  • 今天又惹晴寶寶生氣了,原因在于自己本來說去找她,臨時有事沒有及時告訴她,讓她多等了。事雖然不是特別嚴重的事,但是折...
    亂世小民閱讀 130評論 2 0