最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

6大數(shù)據(jù)庫挖掘7種業(yè)務(wù)場景的存儲更優(yōu)解天生我材必有用,千金散盡還復(fù)來。

2023-03-03 14:28 作者:山觀那恭喜囧昂貴的  | 我要投稿


一文讀懂NodeJs學(xué)問體系和原理淺析
node.js 初探


6大數(shù)據(jù)庫挖掘7種業(yè)務(wù)場景的存儲更優(yōu)解

download:https://www.zxit666.com/5631/

Node.js 是一個 JS 的效勞端運(yùn)轉(zhuǎn)環(huán)境,簡單的來說,它是在 JS 言語標(biāo)準(zhǔn)的根底上,封裝了一些效勞端的運(yùn)轉(zhuǎn)時對象,讓我們可以簡單完成十分多的業(yè)務(wù)功用。

假如我們只運(yùn)用 JS 的話,實(shí)踐上只是能停止一些簡單的邏輯運(yùn)算。node.js 就是基于 JS 語法增加與操作系統(tǒng)之間的交互。

node.js 的裝置
我們能夠運(yùn)用多種方式來裝置 node.js,node.js 實(shí)質(zhì)上也是一種軟件,我們能夠運(yùn)用直接下載二進(jìn)制裝置文件裝置,經(jīng)過系統(tǒng)包管理停止裝置或者經(jīng)過源碼自行編譯均可。

普通來講,關(guān)于個人開發(fā)的電腦,我們引薦直接經(jīng)過 node.js 官網(wǎng)的二進(jìn)制裝置文件來裝置。關(guān)于打包上線的一些 node.js 環(huán)境,也能夠經(jīng)過二進(jìn)制編譯的方式來裝置。

裝置勝利之后,我們的 node 命令就會自動參加我們的系統(tǒng)環(huán)境變量 path 中,我們就能直接在全局運(yùn)用 node 命令訪問到我們方才裝置的 node 可執(zhí)行命令行工具。

node.js 版本切換在個人電腦上,我們能夠裝置一些工具,對 node.js 版本停止切換,例如 nvm 和 n。


nvm 的全稱就是 node version manager,意義就是可以管理 node 版本的一個工具,它提供了一種直接經(jīng)過 shell 執(zhí)行的方式來停止裝置。簡單來說,就是經(jīng)過將多個 node 版本裝置在指定途徑,然后經(jīng)過 nvm 命令切換時,就會切換我們環(huán)境變量中 node 命令指定的實(shí)踐執(zhí)行的軟件途徑。
裝置勝利之后,我們就能在當(dāng)前的操作系統(tǒng)中運(yùn)用多個 node.js 版本。


我們對 npm 應(yīng)該都比擬熟習(xí)了,它是 node.js 內(nèi)置的一款工具,目的在于裝置和發(fā)布契合 node.js 規(guī)范的模塊,從而完成社區(qū)共建的目的繁榮整個社區(qū)。

npx 是 npm@5 之后新增的一個命令,它使得我們能夠在不裝置模塊到當(dāng)前環(huán)境的前提下,運(yùn)用一些 cli 功用。

例如 npx create-react-app some-repo

node.js 的底層依賴
node.js 的主要依賴子模塊有以下內(nèi)容:

V8 引擎
主要是 JS 語法的解析,有了它才干辨認(rèn) JS 語法\

libuv
c 言語完成的一個高性能異步非阻塞 IO 庫,用來完成 node.js 的事情循環(huán)

http-parser/llhttp
底層處置 http 懇求,處置報文, 解析懇求包等內(nèi)容

openssl
處置加密算法,各種框架運(yùn)用普遍

zlib
處置緊縮等內(nèi)容 node.js 常?內(nèi)置模塊

主要模塊
node.js 中最主要的內(nèi)容,就是完成了一套 CommonJS 的模塊化標(biāo)準(zhǔn),以及內(nèi)置了一些常?的模塊。

fs:
文件系統(tǒng),可以讀取寫入當(dāng)前裝置系統(tǒng)環(huán)境中硬 盤的數(shù)據(jù)\

path:
途徑系統(tǒng),可以處置途徑之間的問題

crypto:
加密相關(guān)模塊,可以以規(guī)范的加密方式對我 們的內(nèi)容停止加解密

dns:
處置 dns 相關(guān)內(nèi)容,例如我們能夠設(shè)置 dns 服 務(wù)器等等\

http:
設(shè)置一個 http 效勞器,發(fā)送 http 懇求,監(jiān)聽 響應(yīng)等等

readline:
讀取 stdin 的一行內(nèi)容,能夠讀取、增加、 刪除我們命令行中的內(nèi)容\

os:
操作系統(tǒng)層面的一些 api,例如通知你當(dāng)前系統(tǒng)類 型及一些參數(shù)

vm:
一個專?處置沙箱的虛擬機(jī)模塊,底層主要來調(diào) 用 v8 相關(guān) api 停止代碼解析。

V8 引擎:
引擎只是解析層面,詳細(xì)的上層還有許多詳細(xì)環(huán)境的封裝。

Debug & 內(nèi)存走漏
關(guān)于閱讀器的 JS 代碼來說,我們能夠經(jīng)過斷點(diǎn)停止分步伐試,每一步打印當(dāng)前上下文中的變量結(jié)果,來定位詳細(xì)問題呈現(xiàn)在哪一步。

我們能夠借助 VSCode 或者自行打斷點(diǎn)的方式,來停止分步 node.js 調(diào)試。

關(guān)于 JS 內(nèi)存走漏,我們也能夠運(yùn)用同樣的道理,借助工具,打印每次的內(nèi)存快照,比照得出代碼中的問題。

另一種 JS 解析引擎 quickjs
quickjs 是一個 JS 的解析引擎,輕量代碼量也不大,與之功用相似的就是 V8 引擎。

他最大的特性就是,十分十分輕量,這點(diǎn)從源碼中也能表現(xiàn),事實(shí)上并沒有太多的代碼,它的主要特性和優(yōu)勢:

輕量而且易于嵌入: 只需幾個C文件,沒有外部依賴,一個x86下的簡單的“hello world”程序只需180 KiB
具有極低啟動時間的快速解釋器: 在一臺單核的臺式PC上,大約在100秒內(nèi)運(yùn)轉(zhuǎn)ECMAScript 測試套件156000次的運(yùn)轉(zhuǎn)時實(shí)例完好生命周期在不到300微秒的時間內(nèi)完成。
簡直完好完成ES2019支持,包括: 模塊,異步生成器和和完好Annex B(MPEG-2 transport stream format格式)支持 (傳統(tǒng)的Web兼容性)。許多ES2020中帶來的特性也仍然會被支持。 經(jīng)過100%的ECMAScript Test Suite測試。 能夠?qū)avascript源編譯為沒有外部依賴的可執(zhí)行文件。
另一類 JS 運(yùn)轉(zhuǎn)時效勞端環(huán)境 deno
deno 是一類相似于 node.js 的 JS 運(yùn)轉(zhuǎn)時環(huán)境,同時它也是由 node.js 之父一手打造出來的,他和 node.js 比有什么區(qū)別呢?

相同點(diǎn):
deno 也是基于 V8 ,上層封裝一些系統(tǒng)級別的調(diào)用我們的 deno 應(yīng)用也能夠運(yùn)用 JS 開發(fā)
不同點(diǎn):
deno 基于 rust 和 typescript 開發(fā)一些上層模塊,所以我們能夠直接在 deno 應(yīng)用中書寫 ts
deno 支持從 url 加載模塊,同時支持 top level await 等特性
全局對象解析
JavaScript 中有一個特殊的對象,稱為全局對象(Global Object),它及其一切屬性都能夠在程序的任何中央訪問,即全局變量。

在閱讀器 JavaScript 中,通常 window 是全局對象, 而 Node.js 中的全局對象是 global,一切全局變量(除了 global 自身以外)都是 global 對象的屬性。

在 Node.js 我們能夠直接訪問到 global 的屬性,而不需求在應(yīng)用中包含它。

全局對象和全局變量
global 最基本的作用是作為全局變量的宿主。依照 ECMAScript 的定義,滿足以下條 件的變量是全局變量:

在最外層定義的變量;
全局對象的屬性;
隱式定義的變量(未定義直接賦值的變量)。
當(dāng)你定義一個全局變量時,這個變量同時也會成為全局對象的屬性,反之亦然。需求注 意的是,在 Node.js 中你不可能在最外層定義變量,由于一切用戶代碼都是屬于當(dāng)前模塊的, 而模塊自身不是最外層上下文。

留意: 永遠(yuǎn)運(yùn)用 var 定義變量以防止引入全局變量,由于全局變量會污染 命名空間,進(jìn)步代碼的耦合風(fēng)險。

__filename
__filename 表示當(dāng)前正在執(zhí)行的腳本的文件名。它將輸出文件所在位置的絕對途徑,且和命令行參數(shù)所指定的文件名不一定相同。 假如在模塊中,返回的值是模塊文件的途徑。

console.log( __filename );
__dirname
__dirname 表示當(dāng)前執(zhí)行腳本所在的目錄。

console.log( __dirname );
setTimeout(cb, ms)
setTimeout(cb, ms) 全局函數(shù)在指定的毫秒(ms)數(shù)后執(zhí)行指定函數(shù)(cb)。:setTimeout() 只執(zhí)行一次指定函數(shù)。

返回一個代表定時器的句柄值。

function printHello(){
console.log( "Hello, World!");
}
// 兩秒后執(zhí)行以上函數(shù)
setTimeout(printHello, 2000);
clearTimeout、setInterval、clearInterval、console 在js中比擬常見,故不做展開。

process
process 是一個全局變量,即 global 對象的屬性。

它用于描繪當(dāng)前Node.js 進(jìn)程狀態(tài)的對象,提供了一個與操作系統(tǒng)的簡單接口。通常在你寫本地命令行程序的時分,少不了要 和它打交道。下面將會引見 process 對象的一些最常用的成員辦法。

exit
當(dāng)進(jìn)程準(zhǔn)備退出時觸發(fā)。
beforeExit
當(dāng) node 清空事情循環(huán),并且沒有其他布置時觸發(fā)這個事情。通常來說,當(dāng)沒有進(jìn)程布置時 node 退出,但是 ‘beforeExit’ 的監(jiān)聽器能夠異步伐用,這樣 node 就會繼續(xù)執(zhí)行。
uncaughtException
當(dāng)一個異常冒泡回到事情循環(huán),觸發(fā)這個事情。假如給異常添加了監(jiān)視器,默許的操作(打印堆棧跟蹤信息并退出)就不會發(fā)作。
Signal 事情
當(dāng)進(jìn)程接納到信號時就觸發(fā)。信號列表詳見規(guī)范的 POSIX 信號名,如 SIGINT、SIGUSR1 等。
參考 前端進(jìn)階面試題細(xì)致解答

process.on('exit', function(code) {
// 以下代碼永遠(yuǎn)不會執(zhí)行
setTimeout(function() {
console.log("該代碼不會執(zhí)行");
}, 0);
console.log('退出碼為:', code);
});
console.log("程序執(zhí)行完畢");
退出的狀態(tài)碼
Uncaught Fatal Exception
有未捕獲異常,并且沒有被域或 uncaughtException 處置函數(shù)處置。
Internal JavaScript Parse Error
JavaScript的源碼啟動 Node 進(jìn)程時惹起解析錯誤。十分稀有,僅會在開發(fā) Node 時才會有。
Internal JavaScript Evaluation Failure
JavaScript 的源碼啟動 Node 進(jìn)程,評價時返回函數(shù)失敗。十分稀有,僅會在開發(fā) Node 時才會有。
Fatal Error
V8 里致命的不可恢復(fù)的錯誤。通常會打印到 stderr ,內(nèi)容為: FATAL ERROR
Non-function Internal Exception Handler
未捕獲異常,內(nèi)部異常處置函數(shù)不知為何設(shè)置為on-function,并且不能被調(diào)用。
Internal Exception Handler Run-Time Failure
未捕獲的異常, 并且異常處置函數(shù)處置時本人拋出了異常。例如,假如 process.on(‘uncaughtException’) 或 domain.on(‘error’) 拋出了異常。
Invalid Argument
可能是給了未知的參數(shù),或者給的參數(shù)沒有值。
Internal JavaScript Run-Time Failure
JavaScript的源碼啟動 Node 進(jìn)程時拋出錯誤,十分稀有,僅會在開發(fā) Node 時才會有。
Invalid Debug Argument
設(shè)置了參數(shù)–debug 和/或 –debug-brk,但是選擇了錯誤端口。
Signal Exits
假如 Node 接納到致命信號,比方SIGKILL 或 SIGHUP,那么退出代碼就是128 加信號代碼。這是規(guī)范的 Unix 做法,退出信號代碼放在高位。
// 輸出到終端
process.stdout.write("Hello World!" + "\n");
// 經(jīng)過參數(shù)讀取
process.argv.forEach(function(val, index, array) {
console.log(index + ': ' + val);
});
// 獲取執(zhí)行路局
console.log(process.execPath);
// 平臺信息
console.log(process.platform);
試試看這段代碼輸出什么
// this in NodeJS global scope is the current module.exports object, not the global object.
console.log(this); // {}
module.exports.foo = 5;
console.log(this); // { foo:5 }
Buffer
在理解Nodejs的Buffer之前, 先看幾個根本概念。

背景學(xué)問
1. ArrayBuffer
ArrayBuffer 對象用來表示通用的、固定長度的原始二進(jìn)制數(shù)據(jù)緩沖區(qū)。

ArrayBuffer 不能直接操作,而是要經(jīng)過類型數(shù)組對象或 DataView 對象來操作,它們會將緩沖區(qū)中的數(shù)據(jù)表示為特定的格式,并經(jīng)過這些格式來讀寫緩沖區(qū)的內(nèi)容。

能夠把它了解為一塊內(nèi)存, 詳細(xì)存什么需求其他的聲明。

new ArrayBuffer(length)
// 參數(shù):length 表示要創(chuàng)立的 ArrayBuffer 的大小,單位為字節(jié)。
// 返回值:一個指定大小的 ArrayBuffer 對象,其內(nèi)容被初始化為 0。
// 異常:假如 length 大于 Number.MAX_SAFE_INTEGER(>= 2 ** 53)或為負(fù)數(shù),則拋出一個 RangeError 異常。
ex. 比方這段代碼, 能夠執(zhí)行一下看看輸出什么

var buffer = new ArrayBuffer(8);
var view = new Int16Array(buffer);
console.log(buffer);
console.log(view);
2. Unit8Array
Uint8Array 數(shù)組類型表示一個 8 位無符號整型數(shù)組,創(chuàng)立時內(nèi)容被初始化為 0。
創(chuàng)立完后,能夠?qū)ο蟮姆绞交蜻\(yùn)用數(shù)組下標(biāo)索引的方式援用數(shù)組中的元素。

// 來自長度
var uint8 = new Uint8Array(2);
uint8[0] = 42;
console.log(uint8[0]); // 42
console.log(uint8.length); // 2
console.log(uint8.BYTES_PER_ELEMENT); // 1
// 來自數(shù)組
var arr = new Uint8Array([21,31]);
console.log(arr[1]); // 31
// 來自另一個 TypedArray
var x = new Uint8Array([21, 31]);
var y = new Uint8Array(x);
console.log(y[0]); // 21
3. ArrayBuffer 和 TypedArray
TypedArray: Unit8Array, Int32Array這些都是TypedArray, 那些 Uint32Array 也好,Int16Array 也好,都是給 ArrayBuffer 提供了一個 “View”,MDN上的原話叫做 “Multiple views on the same data”,對它們停止下標(biāo)讀寫,最終都會反響到它所樹立在的 ArrayBuffer 之上。

ArrayBuffer 自身只是一個 0 和 1 寄存在一行里面的一個匯合,ArrayBuffer 不曉得第一個和第二個元素在數(shù)組中該如何分配。

為了能提供上下文,我們需求將其封裝在一個叫做 View 的東西里面。這些在數(shù)據(jù)上的 View 能夠被添加進(jìn)肯定類型的數(shù)組,而且我們有很多種肯定類型的數(shù)據(jù)能夠運(yùn)用。

總結(jié)
總之, ArrayBuffer 根本上扮演了一個原生內(nèi)存的角色.

NodeJs Buffer
Buffer 類以一種更優(yōu)化、更合適 Node.js 用例的方式完成了 Uint8Array API.

Buffer 類的實(shí)例相似于整數(shù)數(shù)組,但 Buffer 的大小是固定的、且在 V8 堆外分配物理內(nèi)存。

Buffer 的大小在被創(chuàng)立時肯定,且無法調(diào)整。

根本運(yùn)用
// 創(chuàng)立一個長度為 10、且用 0 填充的 Buffer。
const buf1 = Buffer.alloc(10);
// 創(chuàng)立一個長度為 10、且用 0x1 填充的 Buffer。
const buf2 = Buffer.alloc(10, 1);
// 創(chuàng)立一個長度為 10、且未初始化的 Buffer。
// 這個辦法比調(diào)用 Buffer.alloc() 更快,
// 但返回的 Buffer 實(shí)例可能包含舊數(shù)據(jù),
// 因而需求運(yùn)用 fill() 或 write() 重寫。
const buf3 = Buffer.allocUnsafe(10);
// 創(chuàng)立一個包含 [0x1, 0x2, 0x3] 的 Buffer。
const buf4 = Buffer.from([1, 2, 3]);
// 創(chuàng)立一個包含 UTF-8 字節(jié) 的 Buffer。
const buf5 = Buffer.from('tést');
tips
當(dāng)調(diào)用 Buffer.allocUnsafe() 時,被分配的內(nèi)存段是未初始化的(沒有用 0 填充)。

固然這樣的設(shè)計使得內(nèi)存的分配十分快,但已分配的內(nèi)存段可能包含潛在的敏感舊數(shù)據(jù)。 運(yùn)用經(jīng)過 Buffer.allocUnsafe() 創(chuàng)立的沒有被完整重寫內(nèi)存的 Buffer ,在 Buffer內(nèi)存可讀的狀況下,可能泄露它的舊數(shù)據(jù)。
固然運(yùn)用 Buffer.allocUnsafe() 有明顯的性能優(yōu)勢,但必需額外當(dāng)心,以防止給應(yīng)用程序引入平安破綻。

Buffer 與字符編碼
Buffer 實(shí)例普通用于表示編碼字符的序列,比方 UTF-8 、 UCS2 、 Base64 、或十六進(jìn)制編碼的數(shù)據(jù)。 經(jīng)過運(yùn)用顯式的字符編碼,就能夠在 Buffer 實(shí)例與普通的 JavaScript 字符串之間停止互相轉(zhuǎn)換。

const buf = Buffer.from('hello world', 'ascii');
console.log(buf)
// 輸出 68656c6c6f20776f726c64
console.log(buf.toString('hex'));
// 輸出 aGVsbG8gd29ybGQ=
console.log(buf.toString('base64'));
Buffer 與字符編碼
Buffer 實(shí)例普通用于表示編碼字符的序列,比方 UTF-8 、 UCS2 、 Base64 、或十六進(jìn)制編碼的數(shù)據(jù)。 經(jīng)過運(yùn)用顯式的字符編碼,就能夠在 Buffer 實(shí)例與普通的 JavaScript 字符串之間停止互相轉(zhuǎn)換。

const buf = Buffer.from('hello world', 'ascii');
console.log(buf)
// 輸出 68656c6c6f20776f726c64
console.log(buf.toString('hex'));
// 輸出 aGVsbG8gd29ybGQ=
console.log(buf.toString('base64'));
Node.js 目前支持的字符編碼包括:

'ascii' - 僅支持 7 位 ASCII 數(shù)據(jù)。假如設(shè)置去掉高位的話,這種編碼是十分快的。
'utf8' - 多字節(jié)編碼的 Unicode 字符。許多網(wǎng)頁和其他文檔格式都運(yùn)用 UTF-8 。
'utf16le' - 2 或 4 個字節(jié),小字節(jié)序編碼的 Unicode 字符。支持代理對(U+10000 至 U+10FFFF)。
'ucs2' - 'utf16le' 的別名。
'base64' - Base64 編碼。當(dāng)從字符串創(chuàng)立 Buffer 時,依照 RFC4648 第 5 章的規(guī)則,這種編碼也將正確地承受 “URL 與文件名平安字母表”。
'latin1' - 一種把 Buffer 編碼成一字節(jié)編碼的字符串的方式(由 IANA 定義在 RFC1345 第 63 頁,用作 Latin-1 補(bǔ)充塊與 C0/C1 控制碼)。
'binary' - 'latin1' 的別名。
'hex' - 將每個字節(jié)編碼為兩個十六進(jìn)制字符。
Buffer 內(nèi)存管理
在引見 Buffer 內(nèi)存管理之前,我們要先來引見一下 Buffer 內(nèi)部的 8K 內(nèi)存池。

8K 內(nèi)存池
在 Node.js 應(yīng)用程序啟動時,為了便當(dāng)?shù)?、高效地運(yùn)用 Buffer,會創(chuàng)立一個大小為 8K 的內(nèi)存池。
Buffer.poolSize = 8 * 1024; // 8K
var poolSize, poolOffset, allocPool;
// 創(chuàng)立內(nèi)存池
function createPool() {
poolSize = Buffer.poolSize;
allocPool = createUnsafeArrayBuffer(poolSize);
poolOffset = 0;
}
createPool();
在 createPool() 函數(shù)中,經(jīng)過調(diào)用 createUnsafeArrayBuffer() 函數(shù)來創(chuàng)立 poolSize(即8K)的 ArrayBuffer 對象。createUnsafeArrayBuffer() 函數(shù)的完成如下:
function createUnsafeArrayBuffer(size) {
zeroFill[0] = 0;
try {
return new ArrayBuffer(size); // 創(chuàng)立指定size大小的ArrayBuffer對象,其內(nèi)容被初始化為0。
} finally {
zeroFill[0] = 1;
}
}
這里你只需曉得 Node.js 應(yīng)用程序啟動時,內(nèi)部有個 8K 的內(nèi)存池即可。

前面簡單引見了 ArrayBuffer 和 Unit8Array 相關(guān)的根底學(xué)問,而 ArrayBuffer 的應(yīng)用在 8K 的內(nèi)存池局部的曾經(jīng)引見過了。那接下來當(dāng)然要輪到 Unit8Array 了,我們再來回憶一下它的語法:
Uint8Array(length);
Uint8Array(typedArray);
Uint8Array(object);
Uint8Array(buffer [, byteOffset [, length]]);
其實(shí)除了 Buffer 類外,還有一個 FastBuffer 類,該類的聲明如下:

class FastBuffer extends Uint8Array {
constructor(arg1, arg2, arg3) {
super(arg1, arg2, arg3);
}
}
是不是曉得 Uint8Array 用在哪里了,在 FastBuffer 類的結(jié)構(gòu)函數(shù)中,經(jīng)過調(diào)用 Uint8Array(buffer [, byteOffset [, length]]) 來創(chuàng)立 Uint8Array 對象。

那么如今問題來了,F(xiàn)astBuffer 有什么用?它和 Buffer 類有什么關(guān)系?帶著這兩個問題,我們先來一同剖析下面的簡單示例:
const buf = Buffer.from('semlinker');
console.log(buf); //
為什么輸出了一串?dāng)?shù)字, 我們創(chuàng)立的字符串呢? 來看一下源碼

/** * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError * if value is a number. * Buffer.from(str[, encoding]) * Buffer.from(array) * Buffer.from(buffer) * Buffer.from(arrayBuffer[, byteOffset[, length]]) **/
Buffer.from = function from(value, encodingOrOffset, length) {
if (typeof value === "string") return fromString(value, encodingOrOffset);
// 處置其它數(shù)據(jù)類型,省略異常處置等其它代碼
if (isAnyArrayBuffer(value))
return fromArrayBuffer(value, encodingOrOffset, length);
var b = fromObject(value);
};
能夠看出 Buffer.from() 工廠函數(shù),支持基于多種數(shù)據(jù)類型(string、array、buffer 等)創(chuàng)立 Buffer 對象。關(guān)于字符串類型的數(shù)據(jù),內(nèi)部調(diào)用 fromString(value, encodingOrOffset) 辦法來創(chuàng)立 Buffer 對象。

是時分來會一會 fromString() 辦法了,它內(nèi)部完成如下:

function fromString(string, encoding) {
var length;
if (typeof encoding !== "string" || encoding.length === 0) {
if (string.length === 0) return new FastBuffer();
// 若未設(shè)置編碼,則默許運(yùn)用utf8編碼。
encoding = "utf8";
// 運(yùn)用 buffer binding 提供的辦法計算string的長度
length = byteLengthUtf8(string);
} else {
// 基于指定的 encoding 計算string的長度
length = byteLength(string, encoding, true);
if (length === -1)
throw new errors.TypeError("ERR_UNKNOWN_ENCODING", encoding);
if (string.length === 0) return new FastBuffer();
}
// 當(dāng)字符串所需字節(jié)數(shù)大于4KB,則直接停止內(nèi)存分配
if (length >= Buffer.poolSize >>> 1)
// 運(yùn)用 buffer binding 提供的辦法,創(chuàng)立buffer對象
return createFromString(string, encoding);
// 當(dāng)剩余的空間小于所需的字節(jié)長度,則先重新申請8K內(nèi)存
if (length > poolSize - poolOffset)
// allocPool = createUnsafeArrayBuffer(8K); poolOffset = 0;
createPool();
// 創(chuàng)立 FastBuffer 對象,并寫入數(shù)據(jù)。
var b = new FastBuffer(allocPool, poolOffset, length);
const actual = b.write(string, encoding);
if (actual !== length) {
// byteLength() may overestimate. That's a rare case, though.
b = new FastBuffer(allocPool, poolOffset, actual);
}
// 更新pool的偏移
poolOffset += actual;
alignPool();
return b;
所以我們得到這樣的結(jié)論

當(dāng)未設(shè)置編碼的時分,默許運(yùn)用 utf8 編碼;
當(dāng)字符串所需字節(jié)數(shù)大于4KB,則直接停止內(nèi)存分配;
當(dāng)字符串所需字節(jié)數(shù)小于4KB,但超越預(yù)分配的 8K 內(nèi)存池的剩余空間,則重新申請 8K 的內(nèi)存池;
調(diào)用 new FastBuffer(allocPool, poolOffset, length) 創(chuàng)立 FastBuffer 對象,停止數(shù)據(jù)存儲,數(shù)據(jù)勝利保管后,會停止長度校驗、更新 poolOffset 偏移量和字節(jié)對齊等操作。
事情循環(huán)模型
什么是事情循環(huán)
事情循環(huán)使 Node.js 能夠經(jīng)過將操作轉(zhuǎn)移到系統(tǒng)內(nèi)核中來執(zhí)行非阻塞 I/O 操作(雖然 JavaScript 是單線程的)。

由于大多數(shù)現(xiàn)代內(nèi)核都是多線程的,因而它們能夠處置在后臺執(zhí)行的多個操作。 當(dāng)這些操作之一完成時,內(nèi)核會通知 Node.js,以便能夠?qū)⑶‘?dāng)?shù)幕卣{(diào)添加到輪詢隊列中以最終執(zhí)行。

Node.js 啟動時,它將初始化事情循環(huán),處置提供的輸入腳本,這些腳本可能會停止異步 API 調(diào)用,調(diào)度計時器或調(diào)用 process.nextTick, 然后開端處置事情循環(huán)。

┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
每個階段都有一個要執(zhí)行的回調(diào) FIFO 隊列。 雖然每個階段都有其本人的特殊方式,但是通常,當(dāng)事情循環(huán)進(jìn)入給定階段時,它將執(zhí)行該階段特定的任何操作,然后在該階段的隊列中執(zhí)行回調(diào),直到隊列耗盡或執(zhí)行回調(diào)的最大數(shù)量為止。 當(dāng)隊列已為空或到達(dá)回調(diào)限制時,事情循環(huán)將移至下一個階段。

timers:此階段執(zhí)行由 setTimeout 和 setInterval 設(shè)置的回調(diào)。
pending callbacks:執(zhí)行推延到下一個循環(huán)迭代的 I/O 回調(diào)。
idle, prepare, :僅在內(nèi)部運(yùn)用。
poll:取出新完成的 I/O 事情;執(zhí)行與 I/O 相關(guān)的回調(diào)(除了關(guān)閉回調(diào),計時器調(diào)度的回調(diào)和 setImmediate 之外,簡直一切這些回調(diào)) 恰當(dāng)時,node 將在此處阻塞。
check:在這里調(diào)用 setImmediate 回調(diào)。
close callbacks:一些關(guān)閉回調(diào),例如 socket.on('close', ...)。
在每次事情循環(huán)運(yùn)轉(zhuǎn)之間,Node.js 會檢查它能否正在等候任何異步 I/O 或 timers,假如沒有,則將其潔凈地關(guān)閉。

各階段細(xì)致解析
timers 計時器階段
計時器能夠在回調(diào)后面指定時間閾值,但這不是我們希望其執(zhí)行確實(shí)切時間。 計時器回調(diào)將在經(jīng)過指定的時間后盡早運(yùn)轉(zhuǎn)。 但是,操作系統(tǒng)調(diào)度或其他回調(diào)的運(yùn)轉(zhuǎn)可能會延遲它們,即執(zhí)行的實(shí)踐時間不肯定。

const fs = require('fs');
function someAsyncOperation(callback) {
// Assume this takes 95ms to complete
fs.readFile('/path/to/file', callback);
}
const timeoutScheduled = Date.now();
setTimeout(() => {
const delay = Date.now() - timeoutScheduled;
console.log(`${delay}ms have passed since I was scheduled`);
}, 100);
// do someAsyncOperation which takes 95 ms to complete
someAsyncOperation(() => {
const startCallback = Date.now();
// do something that will take 10ms...
while (Date.now() - startCallback < 10) {
// do nothing
}
});
當(dāng)事情循環(huán)進(jìn)入 poll 階段時,它有一個空隊列(fs.readFile 尚未完成),因而它將等候直抵達(dá)到最快的計時器 timer 閾值為止。

等候 95 ms 過去時,fs.readFile 完成讀取文件,并將需求 10ms 完成的其回調(diào)添加到輪詢 (poll) 隊列并執(zhí)行。

回調(diào)完成后,隊列中不再有回調(diào),此時勢件循環(huán)已到達(dá)最早計時器 (timer) 的閾值 (100ms),然后返回到計時器 (timer) 階段以執(zhí)行計時器的回調(diào)。

pending callbacks 階段
此階段執(zhí)行某些系統(tǒng)操作的回調(diào),例如 TCP 錯誤,平常無需關(guān)注。

輪詢 poll 階段
輪詢階段具有兩個主要功用:

計算應(yīng)該阻塞并 I/O 輪詢的時間
處置輪詢隊列 (poll queue) 中的事情
當(dāng)事情循環(huán)進(jìn)入輪詢 (poll) 階段并且沒有任何計時器調(diào)度 (timers scheduled) 時,將發(fā)作以下兩種狀況之一:

假如輪詢隊列 (poll queue) 不為空,則事情循環(huán)將遍歷其回調(diào)隊列,使其同步執(zhí)行,直到隊列用盡或到達(dá)與系統(tǒng)相關(guān)的硬性限制為止。
假如輪詢隊列為空,則會發(fā)作以下兩種狀況之一:
2.1 假如已經(jīng)過 setImmediate 調(diào)度了腳本,則事情循環(huán)將完畢輪詢 poll 階段,并繼續(xù)執(zhí)行 check 階段以執(zhí)行那些調(diào)度的腳本。
2.2 假如腳本并沒有 setImmediate 設(shè)置回調(diào),則事情循環(huán)將等候 poll 隊列中的回調(diào),然后立刻執(zhí)行它們。
一旦輪詢隊列 (poll queue) 為空,事情循環(huán)將檢查哪些計時器 timer 曾經(jīng)到時間。 假如一個或多個計時器 timer 準(zhǔn)備就緒,則事情循環(huán)將返回到計時器階段,以執(zhí)行這些計時器的回調(diào)。

檢查階段 check
此階段允許在輪詢 poll 階段完成后立刻執(zhí)行回調(diào)。 假如輪詢 poll 階段處于閑暇,并且腳本已運(yùn)用 setImmediate 進(jìn)入 check 隊列,則事情循環(huán)可能會進(jìn)入 check 階段,而不是在 poll 階段等候。

setImmediate 實(shí)踐上是一個特殊的計時器,它在事情循環(huán)的單獨(dú)階段運(yùn)轉(zhuǎn)。 它運(yùn)用 libuv API,該 API 方案在輪詢階段完成后執(zhí)行回調(diào)。

通常,在執(zhí)行代碼時,事情循環(huán)最終將抵達(dá)輪詢 poll 階段,在該階段它將等候傳入的銜接,懇求等。但是,假如已運(yùn)用 setImmediate 設(shè)置回調(diào)并且輪詢階段變?yōu)殚e暇,則它將將完畢并進(jìn)入 check 階段,而不是等候輪詢事情。

留意:setImmediate為實(shí)驗性辦法,可能不會被批準(zhǔn)成為規(guī)范,目前只要最新版本的 Internet Explorer 和 Node.js 0.10+ 完成了該辦法。
close callbacks 階段
假如套接字或句柄忽然關(guān)閉(例如 socket.destroy),則在此階段將發(fā)出 'close' 事情。 否則它將經(jīng)過 process.nextTick 發(fā)出。

setImmediate 和 setTimeout 的區(qū)別
setImmediate 和 setTimeout 類似,但是依據(jù)調(diào)用時間的不同,它們的行為也不同。

setImmediate 設(shè)計為在當(dāng)前輪詢 poll 階段完成后執(zhí)行腳本。
setTimeout 方案在以毫秒為單位的最小閾值過去之后運(yùn)轉(zhuǎn)腳本。
Tips: 計時器的執(zhí)行次第將依據(jù)調(diào)用它們的上下文而有所不同。 假如兩者都是主模塊中調(diào)用的,則時序?qū)⒃獾竭M(jìn)程性能的限制.

來看兩個例子:

在主模塊中執(zhí)行

兩者的執(zhí)行次第是不固定的, 可能timeout在前, 也可能immediate在前

setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
在同一個I/O回調(diào)里執(zhí)行

setImmediate總是先執(zhí)行

const fs = require('fs');
fs.readFile(__filename, () => {
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
});
問題:那為什么在外部 (比方主代碼局部 mainline) 這兩者的執(zhí)行次第不肯定呢?

解答:在 主代碼 局部執(zhí)行 setTimeout 設(shè)置定時器 (此時還沒有寫入隊列),與 setImmediate 寫入 check 隊列。

mainline 執(zhí)行完開端事情循環(huán),第一階段是 timers,這時分 timers 隊列可能為空,也可能有回調(diào);
假如沒有那么執(zhí)行 check 隊列的回調(diào),下一輪循環(huán)在檢查并執(zhí)行 timers 隊列的回調(diào);
假如有就先執(zhí)行 timers 的回調(diào),再執(zhí)行 check 階段的回調(diào)。因而這是 timers 的不肯定性招致的。

process.nextTick
process.nextTick 從技術(shù)上講不是事情循環(huán)的一局部。 相反,無論事情循環(huán)的當(dāng)前階段如何,都將在當(dāng)前操作完成之后處置 nextTickQueue

process.nextTick 和 setImmediate 的區(qū)別
process.nextTick 在同一階段立刻觸發(fā)
setImmediate fires on the following iteration or 'tick' of the event loop (在事情循環(huán)接下來的階段迭代中執(zhí)行 - check 階段)。
nextTick在事情循環(huán)中的位置
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
| | idle, prepare │
| └─────────────┬─────────────┘
nextTickQueue nextTickQueue
| ┌─────────────┴─────────────┐
| │ poll │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
│ │ check │
│ └─────────────┬─────────────┘
│ nextTickQueue
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
Microtasks 微任務(wù)
在 Node 范疇,微任務(wù)是來自以下對象的回調(diào):

process.nextTick()
then()
在主線完畢后以及事情循環(huán)的每個階段之后,立刻運(yùn)轉(zhuǎn)微任務(wù)回調(diào)。

resolved 的 promise.then 回調(diào)像微處置一樣執(zhí)行,就像 process.nextTick 一樣。 固然,假如兩者都在同一個微任務(wù)隊列中,則將首先執(zhí)行 process.nextTick 的回調(diào)。

優(yōu)先級 process.nextTick > promise.then

執(zhí)行代碼看看輸出次第
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout0')
setTimeout(function () {
console.log('setTimeout1');
}, 0);
setImmediate(() => console.log('setImmediate'));
}, 0)
process.nextTick(() => console.log('nextTick'));
async1();
new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
})
console.log('script end')
Events
events模塊是node的中心模塊之一,簡直一切常用的node模塊都繼承了events模塊,比方http、fs等。

模塊自身十分簡單,API固然也不少,但常用的就那么幾個,這里舉幾個簡單例子。

例子1:單個事情監(jiān)聽器
var EventEmitter = require('events');
class Man extends EventEmitter {}
var man = new Man();
man.on('wakeup', function(){
console.log('man has woken up');
});
man.emit('wakeup');
// 輸出如下:
// man has woken up
例子2:同個事情,多個事情監(jiān)聽器
能夠看到,事情觸發(fā)時,事情監(jiān)聽器依照注冊的次第執(zhí)行。

var EventEmitter = require('events');
class Man extends EventEmitter {}
var man = new Man();
man.on('wakeup', function(){
console.log('man has woken up');
});
man.on('wakeup', function(){
console.log('man has woken up again');
});
man.emit('wakeup');
// 輸出如下:
// man has woken up
// man has woken up again
例子3:只運(yùn)轉(zhuǎn)一次的事情監(jiān)聽器
var EventEmitter = require('events');
class Man extends EventEmitter {}
var man = new Man();
man.on('wakeup', function(){
console.log('man has woken up');
});
man.once('wakeup', function(){
console.log('man has woken up again');
});
man.emit('wakeup');
man.emit('wakeup');
// 輸出如下:
// man has woken up
// man has woken up again
// man has woken up
例子4:注冊事情監(jiān)聽器前,事情先觸發(fā)
能夠看到,注冊事情監(jiān)聽器前,事情先觸發(fā),則該事情會直接被疏忽。

var EventEmitter = require('events');
class Man extends EventEmitter {}
var man = new Man();
man.emit('wakeup', 1);
man.on('wakeup', function(index){
console.log('man has woken up ->' + index);
});
man.emit('wakeup', 2);
// 輸出如下:
// man has woken up ->2
例子5:異步執(zhí)行,還是次第執(zhí)行
例子很簡單,但十分重要。終究是代碼1先執(zhí)行,還是代碼2先執(zhí)行,這點(diǎn)差別,無論關(guān)于我們了解他人的代碼,還是本人編寫node程序,都十分關(guān)鍵。

理論證明,代碼1先執(zhí)行了

var EventEmitter = require('events');
class Man extends EventEmitter {}
var man = new Man();
man.on('wakeup', function(){
console.log('man has woken up'); // 代碼1
});
man.emit('wakeup');
console.log('woman has woken up'); // 代碼2
// 輸出如下:
// man has woken up
// woman has woken up
例子6:移除事情監(jiān)聽器
var EventEmitter = require('events');
function wakeup(){
console.log('man has woken up');
}
class Man extends EventEmitter {}
var man = new Man();
man.on('wakeup', wakeup);
man.emit('wakeup');
man.removeListener('wakeup', wakeup);
man.emit('wakeup');
// 輸出如下:
// man has woken up
手寫完成EventEmitter
event.js ,運(yùn)用發(fā)布訂閱形式完成,原理十分簡單,就是在內(nèi)部用一個對象存儲事情和回調(diào)的對應(yīng)關(guān)系,并且在適宜的時分停止觸發(fā)。

let effects = [];
function depend(obj) { // 搜集依賴
effects.push(obj);
}
function notify(key, data) { // 執(zhí)行依賴
const fnList = effects.filter(x => x.name === key);
fnList.forEach(list => list.fn(data))
}
export default {
$emit(name, data) {
notify(name, data);
},
$on(name, fn) {
depend({ name, fn });
return () => { this.$off(name, fn) }; // 為了便當(dāng)銷毀事情,將辦法吐出
},
$off(name, fn) {
const fnList = effects.filter(x => x.name === name);
effects = fnList.filter(x => x.fn !== fn);
}
}
};
調(diào)用:

import bus from "./event";
const busoff = bus.$on('effect', (data) => { // TODO ... data.id ... }) // 注冊事情
bus.$emit('effect', { id: xxx }) // 觸發(fā)事情
busoff() // 事情銷毀
Stream
在構(gòu)建較復(fù)雜的系統(tǒng)時,通常將其拆解為功用獨(dú)立的若干局部。這些局部的接口遵照一定的標(biāo)準(zhǔn),經(jīng)過某種方式相連,以共同完成較復(fù)雜的任務(wù)。譬如,shell經(jīng)過管道|銜接各局部,其輸入輸出的標(biāo)準(zhǔn)是文本流。

在Node.js中,內(nèi)置的Stream模塊也完成了相似功用,各局部經(jīng)過.pipe()銜接。

Stream提供了以下四品種型的流:

var Stream = require('stream')
var Readable = Stream.Readable
var Writable = Stream.Writable
var Duplex = Stream.Duplex
var Transform = Stream.Transform
運(yùn)用Stream可完成數(shù)據(jù)的流式處置,如:

var fs = require('fs')
// `fs.createReadStream`創(chuàng)立一個`Readable`對象以讀取`bigFile`的內(nèi)容,并輸出到規(guī)范輸出
// 假如運(yùn)用`fs.readFile`則可能由于文件過大而失敗
fs.createReadStream(bigFile).pipe(process.stdout)
Readable
創(chuàng)立可讀流。

實(shí)例:流式耗費(fèi)迭代器中的數(shù)據(jù)。

'use strict'
const Readable = require('stream').Readable
class ToReadable extends Readable {
constructor(iterator) {
super()
this.iterator = iterator
}
// 子類需求完成該辦法
// 這是消費(fèi)數(shù)據(jù)的邏輯
_read() {
const res = this.iterator.next()
if (res.done) {
// 數(shù)據(jù)源已干涸,調(diào)用`push(null)`通知流
return this.push(null)
}
setTimeout(() => {
// 經(jīng)過`push`辦法將數(shù)據(jù)添加到流中
this.push(res.value + '\n')
}, 0)
}
}
module.exports = ToReadable
實(shí)踐運(yùn)用時,new ToReadable(iterator)會返回一個可讀流,下游能夠流式的耗費(fèi)迭代器中的數(shù)據(jù)。

const iterator = function (limit) {
return {
next: function () {
if (limit--) {
return { done: false, value: limit + Math.random() }
}
return { done: true }
}
}
}(1e10)
const readable = new ToReadable(iterator)
// 監(jiān)聽`data`事情,一次獲取一個數(shù)據(jù)
readable.on('data', data => process.stdout.write(data))
// 一切數(shù)據(jù)均已讀完
readable.on('end', () => process.stdout.write('DONE'))
執(zhí)行上述代碼,將會有100億個隨機(jī)數(shù)源源不時地寫進(jìn)規(guī)范輸出流。

創(chuàng)立可讀流時,需求繼承Readable,并完成_read辦法。

_read辦法是從底層系統(tǒng)讀取詳細(xì)數(shù)據(jù)的邏輯,即消費(fèi)數(shù)據(jù)的邏輯。
在_read辦法中,經(jīng)過調(diào)用push(data)將數(shù)據(jù)放入可讀流中供下游耗費(fèi)。
在_read辦法中,能夠同步伐用push(data),也能夠異步伐用。
當(dāng)全部數(shù)據(jù)都消費(fèi)出來后,必需調(diào)用push(null)來完畢可讀流。
流一旦完畢,便不能再調(diào)用push(data)添加數(shù)據(jù)。
能夠經(jīng)過監(jiān)聽data事情的方式耗費(fèi)可讀流。

在初次監(jiān)聽其data事情后,readable便會持續(xù)不時地調(diào)用_read(),經(jīng)過觸發(fā)data事情將數(shù)據(jù)輸出。
第一次data事情會在下一個tick中觸發(fā),所以,能夠平安地將數(shù)據(jù)輸出前的邏輯放在事情監(jiān)聽后(同一個tick中)。
當(dāng)數(shù)據(jù)全部被耗費(fèi)時,會觸發(fā)end事情。
上面的例子中,process.stdout代表規(guī)范輸出流,實(shí)踐是一個可寫流。下小節(jié)中引見可寫流的用法。

Writable
創(chuàng)立可寫流。

前面經(jīng)過繼承的方式去創(chuàng)立一類可讀流,這種辦法也適用于創(chuàng)立一類可寫流,只是需求完成的是_write(data, enc, next)辦法,而不是_read()辦法。

有些簡單的狀況下不需求創(chuàng)立一類流,而只是一個流對象,能夠用如下方式去做:

const Writable = require('stream').Writable
const writable = Writable()
// 完成`_write`辦法
// 這是將數(shù)據(jù)寫入底層的邏輯
writable._write = function (data, enc, next) {
// 將流中的數(shù)據(jù)寫入底層
process.stdout.write(data.toString().toUpperCase())
// 寫入完成時,調(diào)用`next()`辦法通知傳播入下一個數(shù)據(jù)
process.nextTick(next)
}
// 一切數(shù)據(jù)均已寫入底層
writable.on('finish', () => process.stdout.write('DONE'))
// 將一個數(shù)據(jù)寫入流中
writable.write('a' + '\n')
writable.write('b' + '\n')
writable.write('c' + '\n')
// 再無數(shù)據(jù)寫入流時,需求調(diào)用`end`辦法
writable.end()
上游經(jīng)過調(diào)用writable.write(data)將數(shù)據(jù)寫入可寫流中。write()辦法會調(diào)用_write()將data寫入底層。
在_write中,當(dāng)數(shù)據(jù)勝利寫入底層后,必需調(diào)用next(err)通知流開端處置下一個數(shù)據(jù)。
next的調(diào)用既能夠是同步的,也能夠是異步的。
上游必需調(diào)用writable.end(data)來完畢可寫流,data是可選的。爾后,不能再調(diào)用write新增數(shù)據(jù)。
在end辦法調(diào)用后,當(dāng)一切底層的寫操作均完成時,會觸發(fā)finish事情。
Duplex
創(chuàng)立可讀可寫流。

Duplex實(shí)踐上就是繼承了Readable和Writable的一類流。 所以,一個Duplex對象既可當(dāng)成可讀流來運(yùn)用(需求完成_read辦法),也可當(dāng)成可寫流來運(yùn)用(需求完成_write辦法)。

var Duplex = require('stream').Duplex
var duplex = Duplex()
// 可讀端底層讀取邏輯
duplex._read = function () {
this._readNum = this._readNum || 0
if (this._readNum > 1) {
this.push(null)
} else {
this.push('' + (this._readNum++))
}
}
// 可寫端底層寫邏輯
duplex._write = function (buf, enc, next) {
// a, b
process.stdout.write('_write ' + buf.toString() + '\n')
next()
}
// 0, 1
duplex.on('data', data => console.log('ondata', data.toString()))
duplex.write('a')
duplex.write('b')
duplex.write('x')
duplex.end()
上面的代碼中完成了_read辦法,所以能夠監(jiān)聽data事情來耗費(fèi)Duplex產(chǎn)生的數(shù)據(jù)。 同時,又完成了_write辦法,可作為下游去耗費(fèi)數(shù)據(jù)。

由于它既可讀又可寫,所以稱它有兩端:可寫端和可讀端。 可寫端的接口與Writable分歧,作為下游來運(yùn)用;可讀端的接口與Readable分歧,作為上游來運(yùn)用。

Transform
在上面的例子中,可讀流中的數(shù)據(jù)(0, 1)與可寫流中的數(shù)據(jù)(’a’, ‘b’)是隔分開的,但在Transform中可寫端寫入的數(shù)據(jù)經(jīng)變換后會自動添加到可讀端。 Tranform繼承自Duplex,并曾經(jīng)完成了_read和_write辦法,同時請求用戶完成一個_transform辦法。

'use strict'
const Transform = require('stream').Transform
class Rotate extends Transform {
constructor(n) {
super()
// 將字母挪動`n`個位置
this.offset = (n || 13) % 26
}
// 將可寫端寫入的數(shù)據(jù)變換后添加到可讀端
_transform(buf, enc, next) {
var res = buf.toString().split('').map(c => {
var code = c.charCodeAt(0)
if (c >= 'a' && c <= 'z') {
code += this.offset
if (code > 'z'.charCodeAt(0)) {
code -= 26
}
} else if (c >= 'A' && c <= 'Z') {
code += this.offset
if (code > 'Z'.charCodeAt(0)) {
code -= 26
}
}
return String.fromCharCode(code)
}).join('')
// 調(diào)用push辦法將變換后的數(shù)據(jù)添加到可讀端
this.push(res)
// 調(diào)用next辦法準(zhǔn)備處置下一個
next()
}
}
var transform = new Rotate(3)
transform.on('data', data => process.stdout.write(data))
transform.write('hello, ')
transform.write('world!')
transform.end()
數(shù)據(jù)類型
前面幾節(jié)的例子中,經(jīng)常看到調(diào)用data.toString()。這個toString()的調(diào)用是必需的嗎?

在shell中,用管道(|)銜接上下游。上游輸出的是文本流(規(guī)范輸出流),下游輸入的也是文本流(規(guī)范輸入流)

關(guān)于可讀流來說,push(data)時,data只能是String或Buffer類型,而耗費(fèi)時data事情輸出的數(shù)據(jù)都是Buffer類型。關(guān)于可寫流來說,write(data)時,data只能是String或Buffer類型,_write(data)調(diào)用時傳進(jìn)來的data都是Buffer類型。

也就是說,流中的數(shù)據(jù)默許狀況下都是Buffer類型。產(chǎn)生的數(shù)據(jù)一放入流中,便轉(zhuǎn)成Buffer被耗費(fèi);寫入的數(shù)據(jù)在傳給底層寫邏輯時,也被轉(zhuǎn)成Buffer類型。

但每個結(jié)構(gòu)函數(shù)都接納一個配置對象,有一個objectMode的選項,一旦設(shè)置為true,就能呈現(xiàn)“種瓜得瓜,種豆得豆”的效果。

Readable未設(shè)置objectMode時:
const Readable = require('stream').Readable
const readable = Readable()
readable.push('a')
readable.push('b')
readable.push(null)
readable.on('data', data => console.log(data))
Readable設(shè)置objectMode后:
const Readable = require('stream').Readable
const readable = Readable({ objectMode: true })
readable.push('a')
readable.push('b')
readable.push({})
readable.push(null)
readable.on('data', data => console.log(data))
可見,設(shè)置objectMode后,push(data)的數(shù)據(jù)被原樣地輸出了。此時,能夠消費(fèi)恣意類型的數(shù)據(jù)。

6大數(shù)據(jù)庫挖掘7種業(yè)務(wù)場景的存儲更優(yōu)解天生我材必有用,千金散盡還復(fù)來。的評論 (共 條)

分享到微博請遵守國家法律
江城| 外汇| 峡江县| 嘉祥县| 祁阳县| 泽库县| 崇义县| 靖安县| 克山县| 潢川县| 彭州市| 邳州市| 仙桃市| 叙永县| 汝阳县| 凌源市| 新营市| 汉源县| 光泽县| 时尚| 巴南区| 景德镇市| 诸城市| 延庆县| 家居| 温宿县| 徐州市| 明水县| 巴彦淖尔市| 馆陶县| 东丽区| 个旧市| 新化县| 辽源市| 新和县| 自治县| 浮梁县| 无锡市| 鄯善县| 武冈市| 顺昌县|