Node.js核心入門(一)

前言:

由於之前學習Node.js並無真正意義上的去學習它,而是粗略的學習了npm的經常使用命令和Node.js一些模塊化的語法,所以昨天花了一天的時間看了《Node.js開發指南》一書。經過這本書卻是讓我對Node.js的認識更爲全面,但因爲這本書出版時間過早,有些API已經發生了變化或已經被廢棄,而對於學習Node.js來講,核心部分又是最爲重要的一環,所以我配合官方文檔對這本書的第四章-Node.js核心進行了總結與梳理,因爲水平有限,若有疏漏與錯誤,請指正。node

正文

核心模塊是Node.js的心臟,主要是有一些精簡高效的庫組成(這方面和Python有很大的類似之處),爲Node.js提供了基礎的API。主要內容包括:npm

Node.js核心入門(一)編程

  • 全局對象
  • 經常使用工具
  • 事件機制

Node.js核心入門(二)數組

  • 文件系統訪問
  • HTTP服務器與客戶端

全局對象

全局對象我想學過JavaScript的都知道在瀏覽器是window,在程序的任何地方均可以訪問到全局對象,而在Node.js中,這個全局對象換成了global,全部的全局變量(除了global自己)都是global對象的屬性。而咱們在Node.js中可以直接訪問的對象一般都是global的屬性,如:console,process等。瀏覽器

全局對象與全局變量

global最根本的做用就是做爲全局變量的宿主。按照ECMAScript規範,知足如下條件的變量即爲全局變量:bash

  • 在最外層定義的變量(在Node.js中不存在,由於Node.js的代碼在模塊中執行,不存在在最外層定義變量)
  • 全局對象的屬性
  • 隱式定義的變量(即未定義而直接進行賦值的變量)

當咱們定義一個全局變量的時候,這個全局變量會自動成爲全局變量的屬性。服務器

process

process 對象是一個全局變量,它提供當前 Node.js 進程的相關信息,以及控制當前 Node.js 進程。一般咱們在寫本地命令行程序的時候,少不了和它打交道。下面是它的最經常使用的成員方法:架構

1.process.argv

process.argv 屬性返回一個數組,這個數組包含了啓動Node.js進程時的命令行參數。第一個元素爲process.execPath,第二個元素爲當前執行的JavaScript文件路徑,剩餘的元素爲其餘命令行參數。異步

例如存儲一個名爲argv.js的文件:async

// 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 不會打亂對象原有的繼承關係。
相關文章
相關標籤/搜索