Что такое Buffer в Node.js?
Что такое Buffer
Buffer — это глобальный класс Node.js, предназначенный для работы с сырыми бинарными данными. В отличие от строк JavaScript, которые хранятся в куче V8 и всегда закодированы в UTF-16, Buffer выделяет память вне кучи V8 (через malloc/mmap) и хранит байты напрямую.
Buffer появился ещё до того, как в браузерах появился стандарт TypedArray. Сегодня Buffer наследуется от Uint8Array, поэтому он полностью совместим с WebAPI и стандартными типизированными массивами.
Зачем нужен Buffer
JavaScript исторически был языком для работы с текстом. Но при работе с:
- файловой системой (
fs.readFile) - TCP/UDP-сокетами
- HTTP-потоками
- криптографией
- бинарными протоколами (protobuf, msgpack)
...необходимо оперировать отдельными байтами, а не символами Unicode. Buffer решает эту задачу.
Создание Buffer
// Выделить буфер из 10 байт (заполнен нулями)
const buf1 = Buffer.alloc(10);
// Выделить буфер из 10 байт без инициализации (быстрее, но может содержать мусор)
const buf2 = Buffer.allocUnsafe(10);
// Создать из строки
const buf3 = Buffer.from('Привет', 'utf-8');
// Создать из массива байт
const buf4 = Buffer.from([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
// Создать из другого Buffer или Uint8Array
const buf5 = Buffer.from(buf3);
Кодировки
Buffer поддерживает несколько кодировок при преобразовании в строку и обратно:
const buf = Buffer.from('Hello');
console.log(buf.toString('utf-8')); // Hello
console.log(buf.toString('hex')); // 48656c6c6f
console.log(buf.toString('base64')); // SGVsbG8=
Поддерживаемые кодировки: utf-8, utf-16le, latin1, base64, base64url, hex, ascii, binary.
Основные операции
const buf = Buffer.alloc(4);
// Запись числа в буфер (Big Endian)
buf.writeUInt32BE(0xDEADBEEF, 0);
// Чтение числа
console.log(buf.readUInt32BE(0).toString(16)); // deadbeef
// Срез (возвращает view, не копию!)
const slice = buf.slice(0, 2);
// Копирование
const dest = Buffer.alloc(2);
buf.copy(dest, 0, 0, 2);
// Сравнение
console.log(buf.equals(dest)); // false
// Конкатенация
const joined = Buffer.concat([buf, dest]);
console.log(joined.length); // 6
Buffer.alloc vs Buffer.allocUnsafe
Buffer.alloc(size) заполняет выделенную память нулями — безопасно, но медленнее. Buffer.allocUnsafe(size) не обнуляет память, поэтому может содержать старые данные из других процессов. Используйте allocUnsafe только тогда, когда вы сразу перезапишете всё содержимое.
Buffer и потоки
Потоки (stream.Readable, stream.Writable) по умолчанию передают данные как Buffer. Это основа для эффективной обработки больших файлов без загрузки всего содержимого в память:
import fs from 'fs';
const readable = fs.createReadStream('big-file.bin');
readable.on('data', (chunk: Buffer) => {
// chunk — это Buffer, обрабатываем порционно
console.log(`Получено ${chunk.length} байт`);
});
Размер буфера и ограничения
Максимальный размер одного Buffer ограничен константой buffer.constants.MAX_LENGTH (обычно ~2 ГБ на 64-битных системах). Создание очень больших буферов может вызвать ошибку ERR_INVALID_ARG_VALUE.
Что хочет услышать интервьюер
Понимание того, что Buffer хранится вне кучи V8 и работает с сырыми байтами
Знание разницы между Buffer.alloc и Buffer.allocUnsafe (безопасность vs производительность)
Понимание связи Buffer с потоками (Streams) и файловой системой
Знание основных кодировок (utf-8, hex, base64) и метода toString
Понимание того, что Buffer наследуется от Uint8Array и совместим с TypedArray
Пример: Создание и преобразование Buffer
// Создание Buffer разными способами
const fromString = Buffer.from('Привет, мир!', 'utf-8');
console.log(fromString.length); // 23 байта, не 12 символов (кириллица — 2 байта/символ)
const fromHex = Buffer.from('deadbeef', 'hex');
console.log(fromHex); // <Buffer de ad be ef>
// Безопасное выделение (заполнено нулями)
const safe = Buffer.alloc(8);
// Небезопасное — быстрее, но требует немедленной записи
const unsafe = Buffer.allocUnsafe(8);
unsafe.writeDoubleBE(3.14, 0); // сразу перезаписываем содержимое
// Конкатенация
const part1 = Buffer.from('Hello');
const part2 = Buffer.from(' World');
const combined = Buffer.concat([part1, part2]);
console.log(combined.toString()); // Hello World
// Преобразование в разные кодировки
const data = Buffer.from('Node.js');
console.log(data.toString('base64')); // Tm9kZS5qcw==
console.log(data.toString('hex')); // 4e6f64652e6a73
Пример: Работа с бинарными файлами через Buffer
import fs from 'fs/promises';
async function processFile(path: string): Promise<void> {
// fs.readFile возвращает Buffer по умолчанию
const buffer: Buffer = await fs.readFile(path);
// Читаем первые 4 байта как magic number (например, PNG: 89 50 4e 47)
const magic = buffer.readUInt32BE(0);
console.log(`Magic bytes: 0x${magic.toString(16).toUpperCase()}`);
// Получаем часть файла без копирования памяти
const header = buffer.subarray(0, 8);
console.log('Header:', header);
// Конвертируем текстовую часть в строку
const textPart = buffer.subarray(8).toString('utf-8');
console.log('Content:', textPart);
}
processFile('./example.bin').catch(console.error);
Типичные ошибки
Путают Buffer.slice (возвращает view, разделяет память) с Buffer.copy или Buffer.subarray — изменение слайса меняет оригинал
Используют new Buffer() — этот конструктор устарел и удалён, нужно использовать Buffer.from/alloc/allocUnsafe
Не указывают кодировку при Buffer.from(string) и не понимают, что по умолчанию используется utf-8
Используют Buffer.allocUnsafe там, где данные могут быть переданы наружу, не перезаписав содержимое — потенциальная утечка памяти
Считают, что Buffer и строка взаимозаменяемы — не учитывают, что length у Buffer — это байты, а не символы


