Node.js 面试题详细答案 - Q9
Q9: 什么是 Buffer?为什么需要它?(处理二进制数据)
Buffer 概述
Buffer 是 Node.js 中用于处理二进制数据的全局对象,类似于整数数组,但对应 V8 堆内存之外的一块原始内存。
为什么需要 Buffer?
1. JavaScript 字符串的局限性
// JavaScript 字符串基于 UTF-16 编码
const str = 'Hello 世界'
console.log('字符串长度:', str.length) // 8
console.log('字节长度:', Buffer.byteLength(str)) // 12
// 字符串无法直接处理二进制数据
const binaryData = new Uint8Array([72, 101, 108, 108, 111])
console.log('二进制数据:', binaryData)
2. 网络和文件 I/O 需要二进制处理
const fs = require('fs')
// 读取二进制文件
fs.readFile('image.jpg', (err, data) => {
if (err) throw err
console.log('文件大小:', data.length, '字节')
console.log('前几个字节:', data.slice(0, 10))
console.log('是否为 Buffer:', Buffer.isBuffer(data)) // true
})
Buffer 的基本使用
创建 Buffer
// 1. 创建指定大小的 Buffer
const buf1 = Buffer.alloc(10)
console.log('空 Buffer:', buf1)
// 2. 创建并填充 Buffer
const buf2 = Buffer.alloc(10, 'a')
console.log('填充 Buffer:', buf2.toString())
// 3. 从数组创建 Buffer
const buf3 = Buffer.from([1, 2, 3, 4, 5])
console.log('数组 Buffer:', buf3)
// 4. 从字符串创建 Buffer
const buf4 = Buffer.from('Hello World')
console.log('字符串 Buffer:', buf4)
console.log('转换为字符串:', buf4.toString())
Buffer 操作
// 创建 Buffer
const buf = Buffer.from('Hello World')
// 读取数据
console.log('第一个字节:', buf[0]) // 72 (H 的 ASCII 码)
console.log('前 5 个字节:', buf.slice(0, 5)) // <Buffer 48 65 6c 6c 6f>
// 修改数据
buf[0] = 104 // 将 H 改为 h
console.log('修改后:', buf.toString()) // "hello World"
// 查找数据
console.log('查找 "World":', buf.indexOf('World')) // 6
实际应用场景
1. 文件操作
const fs = require('fs')
// 读取二进制文件
fs.readFile('data.bin', (err, data) => {
if (err) throw err
console.log('文件大小:', data.length)
console.log('前 4 个字节:', data.readUInt32BE(0))
})
// 写入二进制文件
const data = Buffer.alloc(1024)
data.writeUInt32BE(12345, 0)
data.writeUInt32BE(67890, 4)
fs.writeFile('output.bin', data, (err) => {
if (err) throw err
console.log('文件写入完成')
})
2. 网络通信
const net = require('net')
const server = net.createServer((socket) => {
socket.on('data', (data) => {
console.log('接收到数据:', data)
console.log('数据长度:', data.length)
// 解析二进制协议
const header = data.readUInt32BE(0)
const payload = data.slice(4)
console.log('头部:', header)
console.log('载荷:', payload)
})
})
server.listen(3000)
3. 加密操作
const crypto = require('crypto')
// 生成随机数据
const randomBytes = crypto.randomBytes(16)
console.log('随机字节:', randomBytes)
// 哈希计算
const data = Buffer.from('Hello World')
const hash = crypto.createHash('sha256').update(data).digest()
console.log('哈希值:', hash)
// 加密
const key = crypto.randomBytes(32)
const iv = crypto.randomBytes(16)
const cipher = crypto.createCipher('aes-256-cbc', key)
cipher.setAutoPadding(true)
let encrypted = cipher.update(data)
encrypted = Buffer.concat([encrypted, cipher.final()])
console.log('加密后:', encrypted)
Buffer 与字符串转换
编码转换
const str = 'Hello 世界'
// 不同编码的 Buffer
const utf8 = Buffer.from(str, 'utf8')
const utf16 = Buffer.from(str, 'utf16le')
const ascii = Buffer.from(str, 'ascii')
console.log('UTF-8 长度:', utf8.length) // 12
console.log('UTF-16 长度:', utf16.length) // 18
console.log('ASCII 长度:', ascii.length) // 8
// 转换回字符串
console.log('UTF-8 转回:', utf8.toString('utf8'))
console.log('UTF-16 转回:', utf16.toString('utf16le'))
console.log('ASCII 转回:', ascii.toString('ascii'))
处理不同编码
// 处理不同编码的数据
function processTextData(data, encoding = 'utf8') {
let buffer
if (Buffer.isBuffer(data)) {
buffer = data
} else if (typeof data === 'string') {
buffer = Buffer.from(data, encoding)
} else {
throw new Error('不支持的数据类型')
}
return {
length: buffer.length,
hex: buffer.toString('hex'),
base64: buffer.toString('base64'),
utf8: buffer.toString('utf8'),
}
}
const result = processTextData('Hello World')
console.log(result)
性能优化
内存管理
// 避免频繁创建 Buffer
function inefficient() {
let result = Buffer.alloc(0)
for (let i = 0; i < 1000; i++) {
const chunk = Buffer.from(`chunk-${i}`)
result = Buffer.concat([result, chunk]) // 每次都创建新 Buffer
}
return result
}
// 优化版本
function efficient() {
const chunks = []
for (let i = 0; i < 1000; i++) {
chunks.push(Buffer.from(`chunk-${i}`))
}
return Buffer.concat(chunks) // 一次性合并
}
流式处理
const { Transform } = require('stream')
// 创建转换流处理 Buffer
const bufferTransform = new Transform({
transform(chunk, encoding, callback) {
// 处理 Buffer 数据
const processed = chunk.map((byte) => byte ^ 0xff) // 简单的异或操作
callback(null, processed)
},
})
// 使用流处理大文件
const fs = require('fs')
const input = fs.createReadStream('input.bin')
const output = fs.createWriteStream('output.bin')
input.pipe(bufferTransform).pipe(output)
常见错误和注意事项
1. 内存泄漏
// 错误:保存 Buffer 引用导致内存泄漏
const buffers = []
setInterval(() => {
const buf = Buffer.alloc(1024 * 1024) // 1MB
buffers.push(buf) // 保存引用,导致内存泄漏
}, 1000)
// 正确:及时释放 Buffer 引用
setInterval(() => {
const buf = Buffer.alloc(1024 * 1024)
// 处理 Buffer
processBuffer(buf)
// 不保存引用,自动垃圾回收
}, 1000)
2. 编码问题
// 错误:假设所有数据都是 UTF-8
function processData(data) {
return data.toString() // 可能乱码
}
// 正确:明确指定编码
function processData(data, encoding = 'utf8') {
return data.toString(encoding)
}
总结
- Buffer 作用:处理二进制数据,弥补 JavaScript 字符串的局限性
- 主要用途:文件 I/O、网络通信、加密操作、图像处理
- 创建方式:
Buffer.alloc(),Buffer.from(),Buffer.allocUnsafe() - 编码支持:UTF-8, UTF-16, ASCII, Base64, Hex 等
- 性能考虑:避免频繁创建,合理使用流式处理
- 内存管理:注意引用管理,避免内存泄漏
- 最佳实践:明确编码类型,合理选择创建方式