NodeJS模塊Buffer原理及使用方法解析
Buffer 作為 nodejs 中重要的概念和功能,為開發(fā)者提供了操作二進(jìn)制的能力。本文記錄了幾個(gè)問題,來加深對(duì) Buffer 的理解和使用:
- 認(rèn)識(shí)緩沖器
- 如何申請(qǐng)堆外內(nèi)存
- 如何計(jì)算字節(jié)長度
- 如何計(jì)算字節(jié)長度
- 如何轉(zhuǎn)換字符編碼
- 理解共享內(nèi)存與拷貝內(nèi)存
認(rèn)識(shí) Buffer(緩沖器)
Buffer 是 nodejs 核心 API,它提供我們處理二進(jìn)制數(shù)據(jù)流的功能。Buffer 的使用和 ES2017 的 Uint8Array 非常相似,但由于 node 的特性,專門提供了更深入的 api。
Uint8Array 的字面意思就是:8 位無符號(hào)整型數(shù)組。一個(gè)字節(jié)是 8bit,而字節(jié)的表示也是由兩個(gè) 16 進(jìn)制(4bit)的數(shù)字組成的。
const buf = Buffer.alloc(1);
console.log(buf); // output: <Buffer 00>
如何申請(qǐng)堆外內(nèi)存
Buffer 可以跳出 nodejs 對(duì)堆內(nèi)內(nèi)存大小的限制。nodejs12 提供了 4 種 api 來申請(qǐng)堆外內(nèi)存:
- Buffer.from()
- Buffer.alloc(size[, fill[, encoding]])
- Buffer.allocUnsafe(size)
- Buffer.allocUnsafeSlow(size)
Buffer.alloc vs Buffer.allocUnsafe
在申請(qǐng)內(nèi)存時(shí),可能這片內(nèi)存之前存儲(chǔ)過其他數(shù)據(jù)。如果不清除原數(shù)據(jù),那么會(huì)有數(shù)據(jù)泄漏的安全風(fēng)險(xiǎn);如果清除原數(shù)據(jù),速度上會(huì)慢一些。具體用哪種方式,根據(jù)實(shí)際情況定。
- Buffer.alloc:申請(qǐng)指定大小的內(nèi)存,并且清除原數(shù)據(jù),默認(rèn)填充 0
- Buffer.allocUnsafe:申請(qǐng)指定大小內(nèi)存,但不清除原數(shù)據(jù),速度更快
根據(jù)提供的 api,可以手動(dòng)實(shí)現(xiàn)一個(gè)alloc:
function pollifyAlloc(size, fill = 0, encoding = "utf8") { const buf = Buffer.allocUnsafe(size); buf.fill(fill, 0, size, encoding); return buf; }
Buffer.allocUnsafe vs Buffer.allocUnsafeSlow
從命名上可以直接看出效果,Buffer.allocUnsafeSlow更慢。因?yàn)楫?dāng)使用 Buffer.allocUnsafe 創(chuàng)建新的 Buffer 實(shí)例時(shí),如果要分配的內(nèi)存小于 4KB,則會(huì)從一個(gè)預(yù)分配的 Buffer 切割出來。 這可以避免垃圾回收機(jī)制因創(chuàng)建太多獨(dú)立的 Buffer 而過度使用。
這種方式通過消除跟蹤和清理的需要來改進(jìn)性能和內(nèi)存使用。
如何計(jì)算字節(jié)長度
利用 Buffer,可以獲得數(shù)據(jù)的真實(shí)所占字節(jié)。例如一個(gè)漢字,它的字符長度是 1。但由于是 utf8 編碼的漢字,所以占用 3 個(gè)字節(jié)。
直接利用Buffer.byteLength()可以獲得字符串指定編碼的字節(jié)長度:
const str = "本文原文地址: xxoo521.com";
console.log(Buffer.byteLength(str, "utf8")); // output: 31
console.log(str.length); // output: 19
也可以直接訪問 Buffer 實(shí)例的 length 屬性(不推薦):
console.log(Buffer.from(str, "utf8").length); // output: 31
如何轉(zhuǎn)換字符編碼
Nodejs 當(dāng)前支持的編碼格式有:ascii、utf8、utf16le、ucs2、base64、latin1、binary、hex。其他編碼需要借助三方庫來完成。
下面,是用Buffer.from()和buf.toString()來封裝的 nodejs 平臺(tái)的編碼轉(zhuǎn)換函數(shù):
function trans(str, from = "utf8", to = "utf8") { const buf = Buffer.from(str, from); return buf.toString(to); } // output: 5Y6f5paH5Zyw5Z2AOiB4eG9vNTIxLmNvbQ== console.log(trans("原文地址: xxoo521.com", "utf8", "base64"));
共享內(nèi)存與拷貝內(nèi)存
在生成 Buffer 實(shí)例,操作二進(jìn)制數(shù)據(jù)的時(shí)候,千萬要注意接口是基于共享內(nèi)存,還是基于拷貝底層內(nèi)存。
例如對(duì)于生成 Buffer 實(shí)例的from(),不同類型的參數(shù),nodejs 底層的行為是不同的。
為了更形象地解釋,請(qǐng)看下面兩段代碼。
代碼 1:
const buf1 = Buffer.from("buffer");
const buf2 = Buffer.from(buf1); // 拷貝參數(shù)中buffer的數(shù)據(jù)到新的實(shí)例
buf1[0]++;console.log(buf1.toString()); // output: cuffer
console.log(buf2.toString()); // output: buffer
代碼 2:
const arr = new Uint8Array(1);
arr[0] = 97;const buf1 = Buffer.from(arr.buffer);
console.log(buf1.toString()); // output: aarr[0] = 98;
console.log(buf1.toString()); // output: b
在第二段代碼中,傳入Buffer.from的參數(shù)類型是arrayBuffer。因此Buffer.from僅僅是創(chuàng)建視圖,而不是拷貝底層內(nèi)存。buf1 和 arr 的內(nèi)存是共享的。
在操作 Buffer 的過程中,需要特別注意共享和拷貝的區(qū)別,發(fā)生錯(cuò)誤比較難排查。
以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
從零學(xué)習(xí)node.js之搭建http服務(wù)器(二)
NodeJs是什么我想大家已經(jīng)很了解了,作為一個(gè)前端工程師,想要不斷提升自己,一般都會(huì)接觸到后端服務(wù)器的技術(shù),NodeJs給我們提供了一個(gè)學(xué)習(xí)成本相對(duì)較低的捷徑可走。下面這篇文章主要介紹了node.js中搭建http服務(wù)器的相關(guān)資料,需要的朋友可以參考下。2017-02-02node?NPM庫string-random生成隨機(jī)字符串學(xué)習(xí)使用
這篇文章主要為大家介紹了node?NPM庫string-random生成隨機(jī)字符串學(xué)習(xí)使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07Node.js爬取豆瓣數(shù)據(jù)實(shí)例分析
這篇文章通過實(shí)例給大家詳細(xì)分析了Node.js爬取豆瓣數(shù)據(jù)的過程以及具體方法步驟,有興趣的朋友可以參考學(xué)習(xí)下。2018-03-03詳解基于Koa2開發(fā)微信二維碼掃碼支付相關(guān)流程
這篇文章主要介紹了詳解基于Koa2開發(fā)微信二維碼掃碼支付相關(guān)流程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05