正文
核心模塊是Node.js的心臟,主要是有一些精簡高效的庫組成(這方面和Python有很大的相似之處),為Node.js提供了基礎的API。主要內容包括:
Node.js核心入門(一)
全局對象
常用工具
事件機制
Node.js核心入門(二)
文件系統訪問
HTTP服務器與客戶端
全局對象
全局對象我想學過JavaScript的都知道在瀏覽器是window,在程序的任何地方都可以訪問到全局對象,而在Node.js中,這個全局對象換成了global,所有的全局變量(除了global本身)都是global對象的屬性。而我們在Node.js中能夠直接訪問的對象通常都是global的屬性,如:console,process等。
全局對象與全局變量
global最根本的作用就是作為全局變量的宿主。按照ECMAScript規范,滿足以下條件的變量即為全局變量:
在最外層定義的變量(在Node.js中不存在,因為Node.js的代碼在模塊中執行,不存在在最外層定義變量)
全局對象的屬性
隱式定義的變量(即未定義而直接進行賦值的變量)
當我們定義一個全局變量的時候,這個全局變量會自動成為全局變量的屬性。
process
process 對象是一個全局變量,它提供當前 Node.js 進程的相關信息,以及控制當前 Node.js 進程。通常我們在寫本地命令行程序的時候,少不了和它打交道。下面是它的最常用的成員方法:
1.process.argv
process.argv 屬性返回一個數組,這個數組包含了啟動Node.js進程時的命令行參數。第一個元素為process.execPath,第二個元素為當前執行的JavaScript文件路徑,剩余的元素為其他命令行參數。
例如存儲一個名為argv.js的文件:
// print process.argv
process.argv.forEach((val, index) => {
console.log(`${index}: ${val}`);
});
復制代碼
則命令行運行時這樣的:
$ node process-args.js one two=three four
0: /usr/local/bin/node
1: /Users/mjr/work/node/process-args.js
2: one
3: two=three
4: four
復制代碼
2.process.stdout
process.stdout是標準輸出流,通常我們使用的console.log()輸出打印字符,而process.stdout.write()函數提供了更為底層的接口。
process.stdout.write('請輸入num1的值:');
復制代碼
3.process.stdin
process.stdin是標準輸入流,初始時它是暫停的,要想從標準輸入讀取數據,我們必須恢復流,并手動編寫流的事件響應函數。
/*1:聲明變量*/
var num1, num2;
/*2:向屏幕輸出,提示信息,要求輸入num1*/
process.stdout.write('請輸入num1的值:');
/*3:監聽用戶的輸入*/
process.stdin.on('data', function (chunk) {
if (!num1) {
num1 = Number(chunk);
/*4:向屏幕輸出,提示信息,要求輸入num2*/
process.stdout.write('請輸入num2的值');
} else {
num2 = Number(chunk);
process.stdout.write('結果是:' + (num1 + num2));
}
});
復制代碼
4.process.nextTick(callback[, ...args])
...args 調用 callback時傳遞給它的額外參數 process.nextTick()方法將 callback 添加到"next tick 隊列"。一旦當前事件輪詢隊列的任務全部完成,在next tick隊列中的所有callbacks會被依次調用。這種方式不是setTimeout(fn, 0)的別名。它更加有效率,因此別用setTimeout去代替process.nextTick。事件輪詢隨后的ticks 調用,會在任何I/O事件(包括定時器)之前運行。
console.log('start');
process.nextTick(() => {
console.log('nextTick callback');
});
console.log('scheduled');
// start
// scheduled
// nextTick callback
復制代碼
console
console 模塊提供了一個簡單的調試控制臺,類似于 Web 瀏覽器提供的 JavaScript 控制臺。該模塊導出了兩個特定的組件:
一個 Console 類,包含 console.log() 、 console.error() 和 console.warn() 等方法,可以被用于寫入到任何 Node.js 流。
一個全局的 console 實例,可被用于寫入到 process.stdout 和 process.stderr。全局的 console 使用時無需調用 require('console')。(注意:全局的 console 對象的方法既不總是同步的(如瀏覽器中類似的 API),也不總是異步的(如其他 Node.js 流)。
比如全局下的常見的console實例:
console.log('hello,world');
// hello,world
console.log('hello%s', 'world');
// helloworld
console.error(new Error('錯誤信息'));
// Error: 錯誤信息
const name = '描述';
console.warn(`警告${name}`);
// 警告描述
console.trace() // 向標準錯誤流輸出當前的調用棧
復制代碼
常見的Console類:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello,world');
// hello,world
myConsole.log('hello%s', 'world');
// helloworld
myConsole.error(new Error('錯誤信息'));
// Error: 錯誤信息
const name = '描述';
myConsole.warn(`警告${name}`);
//警告描述
復制代碼
常用工具 util
util 模塊主要用于支持 Node.js 內部 API 的需求。大部分實用工具也可用于應用程序與模塊開發者,用于彌補核心JavaScript的功能的不足。它的可以這樣調用:
const util = require('util');
復制代碼
1.util.inspect(object[, options])
util.inspect() 方法返回 object 的字符串表示,主要用于調試和錯誤輸出。附加的 options 可用于改變格式化字符串的某些方面。它至少接受一個參數objet,即要轉換的參數,而option則是可選的,可選內容如下:
showHidden 如果為 true,則 object 的不可枚舉的符號與屬性也會被包括在格式化后的結果中。默認為 false。
depth 指定格式化 object 時遞歸的次數。這對查看大型復雜對象很有用。默認為 2。若要無限地遞歸則傳入 null。
colors 如果為 true,則輸出樣式使用 ANSI 顏色代碼。默認為 false,可自定義。
customInspect 如果為 false,則 object 上自定義的 inspect(depth, opts) 函數不會被調用。默認為 true。
showProxy 如果為 true,則 Proxy 對象的對象和函數會展示它們的 target 和 handler 對象。默認為 false。
maxArrayLength 指定格式化時數組和 TypedArray 元素能包含的最大數量。默認為 100。設為 null 則顯式全部數組元素。設為 0 或負數則不顯式數組元素。
breakLength 一個對象的鍵被拆分成多行的長度。設為 Infinity 則格式化一個對象為單行。默認為 60。
例如,查看 util 對象的所有屬性:
const util = require('util');
console.log(util.inspect(util, { showHidden: true, depth: null }));
復制代碼
2.util.callbackify(original)
util.callbackify(original)方法將 async 異步函數(或者一個返回值為 Promise 的函數)轉換成遵循 Node.js 回調風格的函數。在回調函數中, 第一個參數 err 為 Promise rejected 的原因 (如果 Promise 狀態為 resolved , err為 null ),第二個參數則是 Promise 狀態為 resolved 時的返回值。例如:
const util = require('util');
async function fn() {
return await Promise.resolve('hello world');
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
if (err) throw err;
console.log(ret);
});
// hello world
復制代碼
注意:
回調函數是異步執行的, 并且有異常堆棧錯誤追蹤. 如果回調函數拋出一個異常, 進程會觸發一個 'uncaughtException' 異常, 如果沒有被捕獲, 進程將會退出。
null 在回調函數中作為一個參數有其特殊的意義, 如果回調函數的首個參數為 Promise rejected 的原因且帶有返回值, 且值可以轉換成布爾值 false, 這個值會被封裝在 Error 對象里, 可以通過屬性 reason 獲取。
function fn() {
return Promise.reject(null);
}
const callbackFunction = util.callbackify(fn);
callbackFunction((err, ret) => {
// 當Promise的rejecte是null時,它的Error與原始值都會被存儲在'reason'中。
err && err.hasOwnProperty('reason') && err.reason === null; // true
});
復制代碼
事件驅動 events
events是Node.js最重要的模塊,原因是Node.js本身架構就是事件式的,大多數 Node.js 核心 API 都采用慣用的異步事件驅動架構,而它提供了唯一的接口,因此堪稱Node.js事件編程的及時。events 模塊不僅用于用戶代碼與 Node.js 下層事件循環的交互,還幾乎被所有的模塊依賴。
所有能觸發事件的對象都是 EventEmitter 類的實例。這些對象開放了一個 eventEmitter.on() 函數,允許將一個或多個函數綁定到會被對象觸發的命名事件上。事件名稱通常是駝峰式的字符串,但也可以使用任何有效的 JavaScript 屬性名。對于每個事件, EventEmitter支持 若干個事件監聽器。當事件發射時,注冊到這個事件的事件監聽器被依次調用,事件參數作 為回調函數參數傳遞。
例如:
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
// eventEmitter.on() 方法用于注冊監聽器
myEmitter.on('event', () => {
console.log('觸發了一個事件!');
});
// eventEmitter.emit() 方法用于觸發事件
myEmitter.emit('event');
復制代碼
下面讓我們來看看EventEmitter最常用的API:
EventEmitter.on(event, listener) 方法可以添加 listener 函數到名為 eventName 的事件的監聽器數組的末尾。不會檢查 listener 是否已被添加。多次調用并傳入相同的 eventName 和 listener 會導致 listener 被添加與調用多次。
emitter.prependListener(eventName, listener)方法可以添加 listener 函數到名為 eventName 的事件的監聽器數組的開頭。不會檢查 listener 是否已被添加。多次調用并傳入相同的 eventName 和 listener 會導致 listener 被添加與調用多次。
eventEmitter.emit() 方法允許將任意參數傳給監聽器函數。當一個普通的監聽器函數被 EventEmitter 調用時,標準的 this 關鍵詞會被設置指向監聽器所附加的 EventEmitter。
// 實例:
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// 打印:
// b
// a
復制代碼
EventEmitter.once(event, listener) 為指定事件注冊一個單次監聽器,即監聽器最多只會觸發一次,觸發后立刻解除該監聽器。
server.once('connection', (stream) => {
console.log('首次調用!');
});
復制代碼
EventEmitter.removeListener(event, listener) 移除指定事件的某個監聽器, listener 必須是該事件已經注冊過的監聽器。(注意:removeListener 最多只會從監聽器數組里移除一個監聽器實例。如果任何單一的監聽器被多次添加到指定 eventName 的監聽器數組中,則必須多次調用 removeListener 才能移除每個實例。)
const callback = (stream) => {
console.log('有連接!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
復制代碼
EventEmitter.removeAllListeners([event]) 移除所有事件的所有監聽器,如果指定 event ,則移除指定事件的所有監聽器
const callback = (stream) => {
console.log('有連接!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback);
復制代碼
error 事件
EventEmitter 定義了一個特殊的事件 error ,它包含了“錯誤”的語義,我們在遇到異常的時候通常會發射 error 事件。當 error被發射時,EventEmitter規定如果沒有響 應的監聽器,Node.js 會把它當作異常,退出程序并打印調用棧。我們一般要為會發射 error 事件的對象設置監聽器,避免遇到錯誤后整個程序崩潰。
var events = require('events');
var emitter = new events.EventEmitter();
emitter.emit('error');
復制代碼
繼承EventEmitter
大多數情況下,我們不會直接使用EventEmitter,而是在對象中繼承它,包括fs,http在內的只要是支持事件響應的核心模塊都是EventEmitter的子類。這樣做的原因有以下兩個:
具有某個實體功能的對象實現事件符合語義,事件的監聽和發射應該是一個對象的方法。
JavaScript 的對象機制是基于原型的,支持部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關系。
來源:https://juejin.cn/post/6844903586283913230