《前后端面試題
》專欄集合了前后端各個知識模塊的面試題,包括html,javascript,css,vue,react,java,Openlayers,leaflet,cesium,mapboxGL,threejs,nodejs,mangoDB,MySQL,Linux… 。
文章目錄
- 一、本文面試題目錄
- 16. Node.js的核心模塊有哪些?請舉例說明其用途。
- 17. `fs`模塊的作用是什么?它的同步和異步方法有什么區別?
- 18. 如何使用`fs`模塊讀取大文件?為什么不推薦用同步方法讀取大文件?
- 19. `path`模塊的常用方法有哪些?它們的作用是什么?
- 20. `http`模塊如何創建一個簡單的Web服務器?
- 21. 如何處理`http`請求中的GET和POST參數?
- 處理GET參數
- 處理POST參數
- 22. `url`模塊的作用是什么?如何解析URL字符串?
- 23. `querystring`模塊如何解析查詢字符串?
- 24. `events`模塊的作用是什么?如何自定義事件?
- 25. 什么是EventEmitter?它的常用方法有哪些?
- 26. `stream`模塊的作用是什么?它有哪幾種類型?
- 27. 為什么說流(Stream)適合處理大文件?請舉例說明流的使用場景。
- 28. `buffer`模塊的作用是什么?它和字符串有什么區別?
- 29. 如何在Node.js中操作Buffer?請舉例說明常用方法。
- 1. 創建Buffer
- 2. 讀寫Buffer
- 3. 復制Buffer
- 4. 切片Buffer
- 5. 拼接Buffer
- 6. 比較Buffer
- 30. `util`模塊的`util.promisify`方法有什么作用?
- 二、120道Node.js面試題目錄列表
一、本文面試題目錄
16. Node.js的核心模塊有哪些?請舉例說明其用途。
Node.js 提供了眾多核心模塊,這些模塊由 Node.js 官方開發,無需額外安裝即可使用,主要用于處理文件、網絡、事件等基礎功能。以下是常用核心模塊及其用途:
fs
:文件系統模塊,用于讀寫文件、操作目錄等(如fs.readFile
讀取文件,fs.writeFile
寫入文件)。path
:路徑處理模塊,用于解析和拼接文件路徑(如path.join
拼接路徑,path.resolve
獲取絕對路徑)。http
:HTTP 協議模塊,用于創建 Web 服務器和客戶端(如http.createServer
創建服務器)。https
:HTTPS 協議模塊,功能類似http
,但支持加密傳輸。url
:URL 解析模塊,用于解析 URL 字符串(如url.parse
解析 URL 為對象)。querystring
:查詢字符串模塊,用于解析和序列化 URL 中的查詢參數(如querystring.parse
解析查詢字符串)。events
:事件處理模塊,提供事件觸發和監聽機制(核心是EventEmitter
類)。stream
:流模塊,用于處理流式數據(如文件流、網絡流),適合大文件處理。buffer
:緩沖區模塊,用于處理二進制數據(如網絡傳輸、文件讀寫中的二進制數據)。util
:工具模塊,提供實用工具函數(如util.promisify
將回調函數轉為 Promise)。os
:操作系統模塊,用于獲取系統信息(如os.cpus
獲取 CPU 信息,os.totalmem
獲取總內存)。process
:進程模塊,提供當前 Node.js 進程的信息和控制方法(如process.env
環境變量,process.exit
退出進程)。
示例:使用多個核心模塊
const fs = require('fs');
const path = require('path');
const os = require('os');// 讀取當前目錄下的文件
const filePath = path.join(__dirname, 'example.txt');
fs.readFile(filePath, 'utf8', (err, data) => {if (err) throw err;console.log('文件內容:', data);
});// 輸出系統信息
console.log('CPU 核心數:', os.cpus().length);
console.log('總內存(GB):', os.totalmem() / (1024 **3).toFixed(2));
17. fs
模塊的作用是什么?它的同步和異步方法有什么區別?
fs
(File System)模塊是 Node.js 用于與文件系統交互的核心模塊,提供了文件讀寫、目錄操作、權限管理等功能。它的方法分為同步和異步兩種形式。
同步方法與異步方法的區別:
區別 | 同步方法 | 異步方法 |
---|---|---|
執行方式 | 阻塞主線程,需等待操作完成后才繼續執行 | 非阻塞主線程,操作在后臺執行,完成后通過回調通知 |
命名規則 | 方法名末尾加 Sync (如 readFileSync ) | 方法名無特殊后綴(如 readFile ) |
錯誤處理 | 通過 try/catch 捕獲錯誤 | 通過回調函數的第一個參數返回錯誤 |
返回值 | 直接返回操作結果 | 結果通過回調函數的參數返回 |
適用場景 | 簡單腳本、初始化操作(不影響并發的場景) | 服務器環境、高并發場景(避免阻塞主線程) |
示例:同步與異步讀取文件
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, 'test.txt');// 同步讀取
try {const data = fs.readFileSync(filePath, 'utf8');console.log('同步讀取結果:', data);
} catch (err) {console.error('同步讀取錯誤:', err);
}// 異步讀取
fs.readFile(filePath, 'utf8', (err, data) => {if (err) {console.error('異步讀取錯誤:', err);return;}console.log('異步讀取結果:', data);
});
18. 如何使用fs
模塊讀取大文件?為什么不推薦用同步方法讀取大文件?
讀取大文件(如幾個 GB 的文件)時,不適合一次性將文件內容加載到內存中,而應使用流(Stream) 進行分塊讀取,以降低內存占用。
使用流讀取大文件的示例:
const fs = require('fs');
const path = require('path');const largeFilePath = path.join(__dirname, 'large-file.txt');// 創建可讀流
const readStream = fs.createReadStream(largeFilePath, {encoding: 'utf8', // 編碼格式highWaterMark: 64 * 1024 // 每次讀取的緩沖區大小(默認64KB)
});// 監聽數據事件(每讀取一塊數據觸發)
readStream.on('data', (chunk) => {console.log(`讀取到 ${chunk.length} 字節的數據`);// 處理數據(如解析、過濾等)
});// 監聽結束事件
readStream.on('end', () => {console.log('文件讀取完成');
});// 監聽錯誤事件
readStream.on('error', (err) => {console.error('讀取錯誤:', err);
});
不推薦用同步方法讀取大文件的原因:
1.** 阻塞主線程 :同步方法(如 readFileSync
)會阻塞 Node.js 主線程,直到文件讀取完成,期間無法處理其他請求,嚴重影響應用的并發能力。
2. 內存占用過高 :同步方法會將整個文件內容加載到內存中,大文件可能導致內存溢出(OOM),使應用崩潰。
3. 性能低下**:一次性讀取大文件需要分配大量內存,且數據處理效率低,而流的分塊處理更輕量、高效。
19. path
模塊的常用方法有哪些?它們的作用是什么?
path
模塊用于處理文件路徑,解決不同操作系統(如 Windows 和 Linux)路徑分隔符差異(\
和 /
)的問題,提供了跨平臺的路徑處理能力。
常用方法及作用:
-
path.join([...paths])
:- 拼接多個路徑片段,自動處理分隔符,返回規范化的路徑。
path.join('/a', 'b', 'c'); // 輸出:'/a/b/c' path.join('/a', '../b'); // 輸出:'/b'(解析上層目錄)
-
path.resolve([...paths])
:- 將路徑片段解析為絕對路徑,以當前工作目錄為基準。
path.resolve('file.txt'); // 輸出:'/當前工作目錄/file.txt' path.resolve('/a', 'b', '../c'); // 輸出:'/a/c'
-
path.basename(path[, ext])
:- 返回路徑中的文件名(包含擴展名),可選參數
ext
可去除擴展名。
path.basename('/a/b/c.txt'); // 輸出:'c.txt' path.basename('/a/b/c.txt', '.txt'); // 輸出:'c'
- 返回路徑中的文件名(包含擴展名),可選參數
-
path.dirname(path)
:- 返回路徑中的目錄部分。
path.dirname('/a/b/c.txt'); // 輸出:'/a/b'
-
path.extname(path)
:- 返回路徑中的文件擴展名(包含
.
),無擴展名則返回空字符串。
path.extname('file.txt'); // 輸出:'.txt' path.extname('file'); // 輸出:'' path.extname('file.md.txt');// 輸出:'.txt'
- 返回路徑中的文件擴展名(包含
-
path.parse(path)
:- 將路徑解析為包含
root
、dir
、base
、name
、ext
的對象。
path.parse('/a/b/c.txt'); // 輸出: // { // root: '/', // dir: '/a/b', // base: 'c.txt', // name: 'c', // ext: '.txt' // }
- 將路徑解析為包含
-
path.format(pathObject)
:- 將
path.parse
生成的對象轉換為路徑字符串。
path.format({ dir: '/a/b', base: 'c.txt' }); // 輸出:'/a/b/c.txt'
- 將
示例:綜合使用 path
模塊
const path = require('path');const filePath = '/user/docs/report.pdf';console.log('文件名:', path.basename(filePath)); // 'report.pdf'
console.log('目錄:', path.dirname(filePath)); // '/user/docs'
console.log('擴展名:', path.extname(filePath)); // '.pdf'
console.log('拼接路徑:', path.join(path.dirname(filePath), 'archive', 'old.pdf'));
// '/user/docs/archive/old.pdf'
20. http
模塊如何創建一個簡單的Web服務器?
http
模塊是 Node.js 用于構建 HTTP 服務器和客戶端的核心模塊,通過 http.createServer()
方法可快速創建 Web 服務器。
創建簡單Web服務器的步驟:
- 引入
http
模塊。 - 使用
http.createServer()
創建服務器實例,傳入請求處理函數(接收req
請求對象和res
響應對象)。 - 調用
server.listen()
方法指定端口和主機,啟動服務器。
示例:
const http = require('http');// 創建服務器
const server = http.createServer((req, res) => {// 設置響應頭(狀態碼 200,內容類型為文本)res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });// 根據 URL 路徑返回不同內容if (req.url === '/') {res.end('歡迎訪問首頁!\n');} else if (req.url === '/about') {res.end('關于我們\n');} else {res.writeHead(404, { 'Content-Type': 'text/plain' });res.end('404 頁面未找到\n');}
});// 啟動服務器,監聽 3000 端口
const PORT = 3000;
server.listen(PORT, 'localhost', () => {console.log(`服務器運行在 http://localhost:${PORT}`);
});
關鍵對象說明:
req
(IncomingMessage):請求對象,包含請求方法、URL、頭信息等(如req.method
、req.url
)。res
(ServerResponse):響應對象,用于發送響應(如res.writeHead()
設置響應頭,res.end()
發送響應體并結束響應)。
運行上述代碼后,訪問 http://localhost:3000
可看到首頁內容,訪問 http://localhost:3000/about
可看到關于頁面。
21. 如何處理http
請求中的GET和POST參數?
在 Node.js 的 http
模塊中,GET 和 POST 參數的處理方式不同:GET 參數位于 URL 中,POST 參數位于請求體中。
處理GET參數
GET 參數通過 URL 的查詢字符串(?key=value&key2=value2
)傳遞,可使用 url
模塊或 querystring
模塊解析。
示例:
const http = require('http');
const url = require('url');
const querystring = require('querystring');const server = http.createServer((req, res) => {if (req.method === 'GET') {// 解析 URL(第二個參數為 true 時自動解析 query 為對象)const parsedUrl = url.parse(req.url, true);const query = parsedUrl.query; // 獲取 GET 參數對象res.writeHead(200, { 'Content-Type': 'application/json' });res.end(JSON.stringify({message: 'GET 請求參數',params: query}));}
});server.listen(3000, () => {console.log('服務器運行在 http://localhost:3000');
});
訪問 http://localhost:3000?name=Alice&age=20
,將返回解析后的 GET 參數。
處理POST參數
POST 參數位于請求體中,需通過監聽 req
對象的 data
事件(接收數據塊)和 end
事件(數據接收完成)來獲取,再用 querystring
解析。
示例:
const http = require('http');
const querystring = require('querystring');const server = http.createServer((req, res) => {if (req.method === 'POST') {let body = '';// 接收數據塊req.on('data', (chunk) => {body += chunk;});// 數據接收完成req.on('end', () => {// 解析 POST 參數(默認解析 form-data 格式)const postData = querystring.parse(body);res.writeHead(200, { 'Content-Type': 'application/json' });res.end(JSON.stringify({message: 'POST 請求參數',params: postData}));});}
});server.listen(3000, () => {console.log('服務器運行在 http://localhost:3000');
});
可通過 POST 工具(如 Postman)發送 name=Bob&age=25
表單數據,服務器將返回解析結果。
注意:對于 JSON 格式的 POST 數據(如 {"name":"Bob"}
),需手動解析:
// 在 end 事件中
const postData = JSON.parse(body); // 適用于 Content-Type: application/json
22. url
模塊的作用是什么?如何解析URL字符串?
url
模塊用于處理 URL 字符串,提供了解析 URL、格式化 URL 等功能,可將 URL 字符串轉換為對象(便于獲取各部分信息),或反之。
主要作用:
- 解析 URL 字符串為對象,提取協議、主機、路徑、查詢參數等部分。
- 將 URL 對象格式化為 URL 字符串。
- 處理不同格式的 URL(如絕對 URL、相對 URL)。
解析URL字符串的方法:
使用 url.parse(urlString[, parseQueryString[, slashesDenoteHost]])
方法:
urlString
:要解析的 URL 字符串。parseQueryString
:可選,默認為false
,若為true
,則將查詢字符串解析為對象。slashesDenoteHost
:可選,默認為false
,用于處理無協議的 URL。
示例:
const url = require('url');const urlString = 'https://user:pass@example.com:8080/path?name=Alice&age=20#hash';// 解析 URL(parseQueryString 設為 true,自動解析查詢參數)
const parsedUrl = url.parse(urlString, true);console.log('解析結果:', parsedUrl);
/* 輸出主要屬性:
{protocol: 'https:', // 協議auth: 'user:pass', // 用戶名:密碼host: 'example.com:8080', // 主機名:端口hostname: 'example.com', // 主機名port: '8080', // 端口pathname: '/path', // 路徑search: '?name=Alice&age=20', // 查詢字符串(含?)query: { name: 'Alice', age: '20' }, // 解析后的查詢參數對象hash: '#hash' // 哈希(含#)
}
*/// 將 URL 對象轉換為字符串
const formattedUrl = url.format(parsedUrl);
console.log('格式化結果:', formattedUrl); // 輸出原始 URL 字符串
注意:Node.js v11.0.0 后推薦使用 WHATWG URL 標準 API(new URL(urlString)
),功能更完善:
const myUrl = new URL('https://example.com/path?name=Alice');
console.log(myUrl.searchParams.get('name')); // 'Alice'(獲取查詢參數)
23. querystring
模塊如何解析查詢字符串?
querystring
模塊用于處理 URL 中的查詢字符串(如 name=Alice&age=20
),提供了解析(字符串轉對象)和序列化(對象轉字符串)功能。
常用方法:
-
querystring.parse(str[, sep[, eq[, options]]])
:- 解析查詢字符串為對象。
str
:要解析的查詢字符串。sep
:分隔符,默認為&
。eq
:鍵值分隔符,默認為=
。options
:可選配置(如maxKeys
限制解析的鍵數量)。
示例:
const querystring = require('querystring');const str = 'name=Alice&age=20&hobby=reading&hobby=sports'; const obj = querystring.parse(str); console.log(obj); // 輸出:{ name: 'Alice', age: '20', hobby: [ 'reading', 'sports' ] }
-
querystring.stringify(obj[, sep[, eq[, options]]])
:- 將對象序列化為查詢字符串。
obj
:要序列化的對象。sep
:分隔符,默認為&
。eq
:鍵值分隔符,默認為=
。
示例:
const querystring = require('querystring');const obj = { name: 'Bob', age: 25, hobby: ['music', 'games'] }; const str = querystring.stringify(obj); console.log(str); // 輸出:'name=Bob&age=25&hobby=music&hobby=games'
-
querystring.escape(str)
:- 對字符串進行 URL 編碼(將特殊字符轉換為 %xx 格式)。
querystring.escape('a b=c'); // 輸出:'a%20b%3Dc'
-
querystring.unescape(str)
:- 對 URL 編碼的字符串進行解碼。
querystring.unescape('a%20b%3Dc'); // 輸出:'a b=c'
注意:querystring
適合解析簡單的查詢字符串,對于復雜場景(如嵌套對象),建議使用 qs
等第三方庫。
24. events
模塊的作用是什么?如何自定義事件?
events
模塊是 Node.js 實現事件驅動架構的核心,提供了事件的觸發、監聽、移除等功能,其核心是 EventEmitter
類。
主要作用:
- 實現對象間的事件通信(發布-訂閱模式)。
- 支持自定義事件,通過觸發事件執行回調函數。
- 是許多 Node.js 核心模塊(如
fs
、http
)的基礎(這些模塊的對象繼承自EventEmitter
)。
自定義事件的步驟:
- 引入
events
模塊,創建EventEmitter
實例。 - 使用
on()
或addListener()
方法監聽事件。 - 使用
emit()
方法觸發事件,可傳遞參數。
示例:
const EventEmitter = require('events');// 創建 EventEmitter 實例
const myEmitter = new EventEmitter();// 監聽 'greet' 事件(第一個參數為事件名,第二個為回調函數)
myEmitter.on('greet', (name) => {console.log(`Hello, ${name}!`);
});// 監聽 'sum' 事件,處理數字相加
myEmitter.on('sum', (a, b) => {console.log(`${a} + ${b} = ${a + b}`);
});// 觸發事件(第一個參數為事件名,后續為傳遞給回調的參數)
myEmitter.emit('greet', 'Alice'); // 輸出:Hello, Alice!
myEmitter.emit('sum', 3, 5); // 輸出:3 + 5 = 8// 一次性事件(觸發后自動移除)
myEmitter.once('single', () => {console.log('這個事件只會觸發一次');
});
myEmitter.emit('single'); // 輸出:這個事件只會觸發一次
myEmitter.emit('single'); // 無輸出(事件已移除)
注意:EventEmitter
有默認的事件監聽器數量限制(默認 10 個),超過時會觸發警告,可通過 setMaxListeners(n)
調整。
25. 什么是EventEmitter?它的常用方法有哪些?
EventEmitter
是 events
模塊提供的一個類,是 Node.js 事件驅動模型的核心,用于實現事件的發布(觸發)和訂閱(監聽)。Node.js 中的許多核心對象(如 http.Server
、fs.ReadStream
)都繼承自 EventEmitter
。
常用方法:
-
on(eventName, listener)
:- 為指定事件注冊一個監聽器(回調函數),可多次調用注冊多個監聽器。
emitter.on('data', (chunk) => { console.log(chunk); });
-
emit(eventName[, ...args])
:- 觸發指定事件,可傳遞多個參數給監聽器。返回
true
表示事件有監聽器,否則為false
。
emitter.emit('data', 'hello'); // 觸發 'data' 事件,傳遞 'hello'
- 觸發指定事件,可傳遞多個參數給監聽器。返回
-
once(eventName, listener)
:- 為指定事件注冊一個一次性監聽器,觸發一次后自動移除。
emitter.once('init', () => { console.log('初始化完成'); });
-
off(eventName, listener)
(或removeListener
):- 移除指定事件的某個監聽器(需傳入監聽器的引用)。
const listener = () => { console.log('監聽'); }; emitter.on('event', listener); emitter.off('event', listener); // 移除監聽器
-
removeAllListeners([eventName])
:- 移除指定事件的所有監聽器,若未指定事件名,則移除所有事件的監聽器。
emitter.removeAllListeners('data'); // 移除 'data' 事件的所有監聽器
-
setMaxListeners(n)
:- 設置事件監聽器的最大數量(默認 10 個),超過時的警告可通過此方法關閉(設為
Infinity
)。
emitter.setMaxListeners(20); // 允許最多 20 個監聽器
- 設置事件監聽器的最大數量(默認 10 個),超過時的警告可通過此方法關閉(設為
-
listeners(eventName)
:- 返回指定事件的所有監聽器數組。
console.log(emitter.listeners('data')); // 輸出 'data' 事件的監聽器數組
示例:綜合使用 EventEmitter
方法
const EventEmitter = require('events');
const emitter = new EventEmitter();// 定義監聽器函數
const logMessage = (msg) => {console.log('收到消息:', msg);
};// 注冊監聽器
emitter.on('message', logMessage);
emitter.on('message', (msg) => {console.log('消息長度:', msg.length);
});// 觸發事件
emitter.emit('message', 'Hello EventEmitter');
// 輸出:
// 收到消息: Hello EventEmitter
// 消息長度: 18// 移除監聽器
emitter.off('message', logMessage);
emitter.emit('message', '再次發送');
// 輸出:
// 消息長度: 4
26. stream
模塊的作用是什么?它有哪幾種類型?
stream
(流)模塊是 Node.js 用于處理流式數據的核心模塊,通過分塊讀取/寫入數據,降低內存占用,提高處理效率,尤其適合大文件或持續的數據流(如網絡傳輸)。
主要作用:
- 分塊處理數據,避免一次性加載大量數據到內存。
- 支持管道(
pipe
)操作,可將一個流的輸出直接作為另一個流的輸入(如文件壓縮、網絡傳輸)。 - 提供統一的接口處理不同類型的數據流(文件、網絡、內存等)。
流的四種類型:
-
Readable(可讀流):
- 用于讀取數據的流(如
fs.createReadStream
讀取文件)。 - 狀態:
paused
(暫停)和flowing
(流動),數據通過data
事件傳遞。
- 用于讀取數據的流(如
-
Writable(可寫流):
- 用于寫入數據的流(如
fs.createWriteStream
寫入文件)。 - 通過
write()
方法寫入數據,end()
方法結束寫入。
- 用于寫入數據的流(如
-
Duplex(雙工流):
- 同時具備可讀和可寫能力的流(如 TCP 套接字
net.Socket
)。 - 可讀和可寫部分相互獨立,分別遵循各自的接口。
- 同時具備可讀和可寫能力的流(如 TCP 套接字
-
Transform(轉換流):
- 一種特殊的雙工流,用于修改或轉換數據(如
zlib.createGzip
壓縮數據)。 - 讀取數據后進行處理,再輸出處理后的結果。
- 一種特殊的雙工流,用于修改或轉換數據(如
示例:使用可讀流和可寫流復制文件
const fs = require('fs');
const path = require('path');// 創建可讀流(源文件)
const readStream = fs.createReadStream(path.join(__dirname, 'source.txt'));
// 創建可寫流(目標文件)
const writeStream = fs.createWriteStream(path.join(__dirname, 'copy.txt'));// 監聽數據事件,將讀取的塊寫入可寫流
readStream.on('data', (chunk) => {writeStream.write(chunk);
});// 讀取完成后結束寫入
readStream.on('end', () => {writeStream.end();console.log('文件復制完成');
});// 簡化寫法:使用 pipe 自動處理數據傳遞
// readStream.pipe(writeStream);
27. 為什么說流(Stream)適合處理大文件?請舉例說明流的使用場景。
流(Stream)適合處理大文件的核心原因是其分塊處理數據的特性,無需將整個文件加載到內存中,而是逐塊讀取、處理和寫入,顯著降低內存占用并提高效率。
流適合處理大文件的原因:
- 低內存占用:大文件(如 10GB)無法一次性加載到內存,流通過每次讀取一小塊數據(默認 64KB),內存占用保持穩定。
- 高效處理:數據可邊讀取邊處理(如解析、過濾),無需等待整個文件加載完成,減少處理延遲。
- 管道操作:支持
pipe()
方法將多個流串聯(如讀取 → 壓縮 → 寫入),簡化大文件處理流程。
流的典型使用場景:
1.** 大文件復制 **:
const fs = require('fs');
// 使用 pipe 快速復制大文件
fs.createReadStream('large-file.iso').pipe(fs.createWriteStream('copy-large-file.iso'));
2.** 日志實時處理 **:
監聽日志文件的新增內容并實時分析(如監控系統)。
const fs = require('fs');
const readStream = fs.createReadStream('app.log', { tail: true }); // 模擬尾行監聽
readStream.on('data', (chunk) => {console.log('新日志:', chunk.toString());// 實時分析邏輯(如錯誤檢測)
});
3.** 數據壓縮/解壓 **:
結合 zlib
模塊對流數據進行壓縮(無需加載整個文件到內存)。
const fs = require('fs');
const zlib = require('zlib');
// 讀取文件 → 壓縮 → 寫入壓縮文件
fs.createReadStream('large-file.txt').pipe(zlib.createGzip()).pipe(fs.createWriteStream('large-file.txt.gz'));
4.** 網絡數據傳輸 **:
HTTP 服務器中通過流發送大文件(如視頻、下載資源)。
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {if (req.url === '/video') {res.writeHead(200, { 'Content-Type': 'video/mp4' });// 以流的形式發送視頻文件fs.createReadStream('large-video.mp4').pipe(res);}
}).listen(3000);
5.** 數據轉換 **:
處理 CSV 大文件并轉換為 JSON 格式。
const fs = require('fs');
const readline = require('readline'); // 逐行讀取流
const rl = readline.createInterface({input: fs.createReadStream('large-data.csv'),crlfDelay: Infinity
});
rl.on('line', (line) => {// 每行轉換為 JSON 并處理const json = convertCsvToJson(line);console.log(json);
});
28. buffer
模塊的作用是什么?它和字符串有什么區別?
buffer
模塊是 Node.js 用于處理二進制數據的核心模塊,提供了 Buffer
類(無需 require
即可使用),用于存儲和操作二進制數據。在處理文件、網絡傳輸、加密等場景中,二進制數據是基礎,Buffer
解決了 JavaScript 原生不支持二進制數據的問題。
Buffer
與字符串的區別:
區別 | Buffer | 字符串(String) |
---|---|---|
數據類型 | 二進制數據(字節序列) | Unicode 字符序列(文本數據) |
編碼方式 | 可通過編碼(如 utf8 、base64 )轉換為字符串 | 本質是 Unicode 編碼,可通過 Buffer.from() 轉為 Buffer |
長度計算 | length 屬性返回字節數 | length 屬性返回字符數(可能與字節數不同) |
不可變性 | 長度固定,內容可修改(內存分配后大小不變) | 不可變,修改會創建新字符串 |
適用場景 | 二進制數據(文件、網絡、加密) | 文本數據(用戶輸入、顯示內容) |
示例:Buffer 與字符串的轉換
// 字符串轉 Buffer(默認 utf8 編碼)
const str = 'Hello 世界';
const buf = Buffer.from(str);
console.log('Buffer 內容:', buf); // <Buffer 48 65 6c 6c 6f 20 e4 b8 96 e7 95 8c>
console.log('Buffer 字節數:', buf.length); // 11('Hello ' 占 6 字節,'世界' 占 6 字節?此處實際為11字節,因UTF8編碼中漢字占3字節)// Buffer 轉字符串
const str2 = buf.toString('utf8');
console.log('轉換后的字符串:', str2); // 'Hello 世界'// 不同編碼的轉換
const base64Str = buf.toString('base64');
console.log('Base64 編碼:', base64Str); // 'SGVsbG8g5LiW55WM'
const buf2 = Buffer.from(base64Str, 'base64');
console.log(buf2.toString('utf8')); // 'Hello 世界'
注意:字符串的 length
可能與對應的 Buffer 字節數不同(如中文在 UTF8 中占 3 字節),例如 '世界'.length
為 2,而 Buffer.from('世界').length
為 6。
29. 如何在Node.js中操作Buffer?請舉例說明常用方法。
Buffer
是 Node.js 中處理二進制數據的核心類,提供了豐富的方法用于創建、讀寫、轉換 Buffer。以下是常用操作及示例:
1. 創建Buffer
Buffer.alloc(size)
:創建指定大小的空 Buffer(已初始化,默認填充 0)。Buffer.allocUnsafe(size)
:創建指定大小的 Buffer(未初始化,可能包含舊數據,速度更快)。Buffer.from(data)
:從數據(字符串、數組、Buffer 等)創建 Buffer。
// 創建 10 字節的空 Buffer
const buf1 = Buffer.alloc(10);
console.log(buf1); // <Buffer 00 00 00 00 00 00 00 00 00 00>// 從字符串創建 Buffer
const buf2 = Buffer.from('hello');
console.log(buf2); // <Buffer 68 65 6c 6c 6f>// 從數組創建 Buffer(數組元素為 0-255 的整數)
const buf3 = Buffer.from([0x68, 0x65, 0x6c]); // 對應 'hel'
console.log(buf3.toString()); // 'hel'
2. 讀寫Buffer
Buffer 可通過索引讀寫字節(0-255 的整數)。
const buf = Buffer.alloc(4);// 寫入字節
buf[0] = 0x48; // 'H' 的 ASCII 碼
buf[1] = 0x65; // 'e'
buf[2] = 0x6c; // 'l'
buf[3] = 0x6c; // 'l'console.log(buf.toString()); // 'Hell'// 讀取字節
console.log(buf[0]); // 72(0x48 的十進制)
console.log(String.fromCharCode(buf[0])); // 'H'
3. 復制Buffer
buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
:復制一個 Buffer 的內容到另一個 Buffer。
const srcBuf = Buffer.from('hello');
const destBuf = Buffer.alloc(5);srcBuf.copy(destBuf); // 復制整個 srcBuf 到 destBuf
console.log(destBuf.toString()); // 'hello'// 部分復制(復制 'llo' 到 destBuf 的起始位置 2)
const destBuf2 = Buffer.alloc(5, 'he'); // 初始化為 'he'
srcBuf.copy(destBuf2, 2, 2, 5); // targetStart=2, sourceStart=2, sourceEnd=5
console.log(destBuf2.toString()); // 'hello'
4. 切片Buffer
buf.slice([start[, end]])
:返回 Buffer 的一部分(與原 Buffer 共享內存,修改會影響原 Buffer)。
const buf = Buffer.from('hello world');
const slice = buf.slice(6, 11); // 從索引 6 到 10(不包含11)
console.log(slice.toString()); // 'world'// 修改切片會影響原 Buffer
slice[0] = 0x57; // 'W' 的 ASCII 碼
console.log(buf.toString()); // 'hello World'
5. 拼接Buffer
Buffer.concat(list[, totalLength])
:拼接多個 Buffer 為一個新 Buffer。
const buf1 = Buffer.from('Hello ');
const buf2 = Buffer.from('World');
const combined = Buffer.concat([buf1, buf2]);
console.log(combined.toString()); // 'Hello World'
6. 比較Buffer
buf.compare(otherBuffer)
:比較兩個 Buffer,返回數值(-1:當前小,0:相等,1:當前大)。
const buf1 = Buffer.from('abc');
const buf2 = Buffer.from('abd');
console.log(buf1.compare(buf2)); // -1('abc' < 'abd')
30. util
模塊的util.promisify
方法有什么作用?
util.promisify
是 Node.js util
模塊提供的工具函數,用于將遵循 Node.js 回調風格(即最后一個參數為回調函數,且回調的第一個參數為錯誤對象)的函數轉換為返回 Promise 的函數,便于使用 async/await
語法處理異步操作。
作用:
- 簡化異步代碼,將回調地獄(Callback Hell)轉換為更易讀的
async/await
形式。 - 統一異步編程風格,兼容 Promise 生態。
使用條件:
被轉換的函數必須符合 Node.js 回調規范:
- 函數的最后一個參數是回調函數。
- 回調函數的第一個參數是錯誤對象(
err
),后續參數是結果。
示例:將回調風格的函數轉為 Promise
const util = require('util');
const fs = require('fs');// 回調風格的函數(fs.readFile)
fs.readFile('example.txt', 'utf8', (err, data) => {if (err) throw err;console.log('回調方式讀取:', data);
});// 使用 util.promisify 轉換為 Promise 風格
const readFileAsync = util.promisify(fs.readFile);// 使用 Promise.then()
readFileAsync('example.txt', 'utf8').then(data => console.log('Promise 方式讀取:', data)).catch(err => console.error('錯誤:', err));// 使用 async/await(更簡潔)
async function readFile() {try {const data = await readFileAsync('example.txt', 'utf8');console.log('async/await 方式讀取:', data);} catch (err) {console.error('錯誤:', err);}
}
readFile();
自定義回調函數的轉換:
const util = require('util');// 自定義回調風格函數(符合規范)
function fetchData(callback) {setTimeout(() => {const err = null;const data = '模擬數據';callback(err, data); // 第一個參數為 err,第二個為結果}, 1000);
}// 轉換為 Promise 函數
const fetchDataAsync = util.promisify(fetchData);// 使用 async/await
async function main() {const data = await fetchDataAsync();console.log(data); // '模擬數據'
}
main();
注意:Node.js v10.0.0 后,許多核心模塊提供了內置的 Promise 版本(如 fs.promises
),可直接使用,無需手動轉換。
二、120道Node.js面試題目錄列表
文章序號 | Node.js面試題120道 |
---|---|
1 | Node.js面試題及詳細答案120道(01-15) |
2 | Node.js面試題及詳細答案120道(16-30) |
3 | Node.js面試題及詳細答案120道(31-42) |
4 | Node.js面試題及詳細答案120道(43-55) |
5 | Node.js面試題及詳細答案120道(56-68) |
6 | Node.js面試題及詳細答案120道(69-80) |
7 | Node.js面試題及詳細答案120道(81-92) |
8 | Node.js面試題及詳細答案120道(93-100) |
9 | Node.js面試題及詳細答案120道(101-110) |
10 | Node.js面試題及詳細答案120道(111-120) |