Di Section Filesystem kita telah melihat bahwa readFile tanpa encoding mengembalikan object Buffer ketimbang string. Moment itu merupakan pertemuan pertama kita dengan cara Node.js menyimpan data biner. Aplikasi nyata tidak hanya berurusan dengan teks: gambar, arsip terkompresi, isi paket jaringan, hingga hasil hash kriptografi semuanya berupa rangkaian byte mentah. Chapter ini membahas Buffer sebagai wadah byte di Node.js: cara membuatnya, membaca isinya, dan hubungan eratnya dengan Uint8Array milik JavaScript.

String untuk Manusia, Byte untuk Mesin #

String dirancang untuk teks yang dibaca manusia, sementara berkas di disk dan data yang lewat di jaringan hanyalah rangkaian byte. Ketiga kendala berikut membuat string tidak cocok menjadi wadah data biner:

  1. Representasi internal string tidak dijamin sama antar platform dan mesin JavaScript.
  2. Operasi biner seperti mengubah satu byte di tengah data menjadi canggung kalau memakai string.
  3. Sebagian rangkaian byte tidak memiliki arti apa pun sebagai teks.

Buffer menjawab ketiga kendala tersebut dengan model yang sederhana: array angka 0 sampai 255 yang menempati memori berurutan. Satu elemen array mewakili satu byte. Karena bentuknya mirip array, kita bisa mengakses, mengubah, dan mengiterasi byte di dalamnya dengan cara yang sudah akrab bagi kita.

Membuat Buffer #

Tiga cara paling umum membuat Buffer adalah Buffer.alloc(), Buffer.allocUnsafe(), dan Buffer.from() — ketiganya dibahas mendalam pada dokumentasi Buffer . Simpan contoh berikut sebagai membuat-buffer.mjs, lalu jalankan dengan node membuat-buffer.mjs:

js
import { Buffer } from "node:buffer";

const kosong = Buffer.alloc(4);
console.log("Buffer.alloc(4)               :", kosong.toString("hex"));

const terisi = Buffer.alloc(4, 0x41);
console.log("Buffer.alloc(4, 0x41)         :", terisi.toString("utf8"));

const dariString = Buffer.from("Node");
console.log("Buffer.from('Node')           :", dariString.toString("hex"));

const dariArray = Buffer.from([78, 111, 100, 101]);
console.log("Buffer.from([78, 111, 100, 101]):", dariArray.toString("utf8"));

Output yang Dihasilkan:

text
Buffer.alloc(4)               : 00000000
Buffer.alloc(4, 0x41)         : AAAA
Buffer.from('Node')           : 4e6f6465
Buffer.from([78, 111, 100, 101]): Node

Mari kita telaah baris-baris kode di atas:

  1. Baris 1 (import { Buffer } from "node:buffer"): Buffer diimpor dari module node:buffer mengikuti konvensi prefix node: pada Course ini. Node.js juga menyediakan Buffer sebagai global, sehingga kode singkat boleh memakainya tanpa import.
  2. Baris 3 (Buffer.alloc(4)): alloc() mengalokasikan Buffer baru sepanjang 4 byte dan mengisi seluruhnya dengan nol — terlihat dari hex 00000000. Pengisian nol inilah yang membuat alloc() aman.
  3. Baris 6 (Buffer.alloc(4, 0x41)): argumen kedua mengganti nilai isi awal. Angka 0x41 adalah 65 dalam desimal, yaitu kode karakter A, sehingga isi Buffer menjadi "AAAA".
  4. Baris 9 (Buffer.from("Node")): from() membuat Buffer dari data yang sudah ada. String "Node" dikonversi menjadi 4 byte UTF-8: 4e 6f 64 65.
  5. Baris 12 (Buffer.from([78, 111, 100, 101])): array angka dibaca sebagai daftar byte mentah, sehingga hasilnya identik dengan versi string — angka 78 adalah 0x4e.

Untuk memakai allocUnsafe() secara bertanggung jawab, seluruh byte harus ditimpa sebelum dibaca. Method fill() menuntaskan pekerjaan tersebut dalam satu panggilan:

js
import { Buffer } from "node:buffer";

const cepat = Buffer.allocUnsafe(4);
cepat.fill(0);
console.log("setelah fill(0):", cepat.toString("hex"));

Output yang Dihasilkan:

text
setelah fill(0): 00000000

Setelah fill(0), isi Buffer menjadi 00000000 dan aman dipakai. Pola allocUnsafe() + fill() masuk akal ketika kita hendak mengisi ulang seluruh Buffer dengan data baru — pengisian nol di awal hanya menjadi kerja yang terbuang.

Mengakses Byte di dalam Buffer #

Buffer bersifat mutable: setiap byte dapat dibaca dan diubah lewat tanda kurung siku, persis seperti elemen array. Program berikut menunjukkan pembacaan, iterasi, dan penulisan byte:

js
import { Buffer } from "node:buffer";

const teks = Buffer.from("Node");

console.log("teks.length :", teks.length);
console.log("teks[0]     :", teks[0]);
console.log("teks[0] hex :", teks[0].toString(16));

let hexManual = "";
for (const byte of teks) {
  hexManual += byte.toString(16).padStart(2, "0");
}
console.log("hex manual  :", hexManual);

teks[0] = 33;
console.log("setelah teks[0] = 33:", teks.toString());

Output yang Dihasilkan:

text
teks.length : 4
teks[0]     : 78
teks[0] hex : 4e
hex manual  : 4e6f6465
setelah teks[0] = 33: !ode

Mari kita telaah baris-baris kode di atas:

  1. Baris 5 (teks.length): properti length menyatakan jumlah byte, bukan jumlah karakter — dua hal yang bisa berbeda untuk teks non-ASCII.
  2. Baris 6-7 (teks[0]): indexing mengembalikan angka desimal 78. Method toString(16) pada angka menghasilkan 4e, padanan hex dari byte pertama.
  3. Baris 9-12 (for (const byte of teks)): Buffer dapat diiterasi karena merupakan typed array. padStart(2, "0") memastikan setiap byte selalu dua digit hex, misalnya 0e dan bukan sekadar e.
  4. Baris 15 (teks[0] = 33): penugasan mengubah byte pertama menjadi 0x21, karakter !. Hasil cetaknya pun berubah menjadi !ode — bukti Buffer benar-benar mutable.

Buffer adalah Uint8Array #

Buffer bukan tipe yang berdiri sendiri. Buffer merupakan subclass dari Uint8Array, typed array standar JavaScript untuk byte tanpa tanda — rincian hubungan ini didokumentasikan pada bagian Buffers and TypedArrays . Hubungan ini dapat dibuktikan langsung dengan instanceof:

js
import { Buffer } from "node:buffer";

const teks = Buffer.from("Node");

console.log("teks instanceof Uint8Array :", teks instanceof Uint8Array);
console.log("Buffer.isBuffer(teks)      :", Buffer.isBuffer(teks));
console.log("teks.subarray(1)           :", teks.subarray(1).toString());
console.log("ArrayBuffer.isView(teks)   :", ArrayBuffer.isView(teks));

Output yang Dihasilkan:

text
teks instanceof Uint8Array : true
Buffer.isBuffer(teks)      : true
teks.subarray(1)           : ode
ArrayBuffer.isView(teks)   : true

Karena mewarisi seluruh method Uint8Array, Buffer mengenal subarray(), set(), hingga slice(). Method subarray(1) mengembalikan tampilan byte mulai indeks 1 tanpa menyalin memori baru — cocok untuk mengintip sebagian data besar. Sebaliknya, method spesifik Buffer seperti toString("hex") tidak dimiliki Uint8Array biasa, sebagaimana dirinci pada rujukan MDN untuk Uint8Array .

Di Mana Buffer Muncul #

Kita akan bertemu Buffer di banyak titik Node.js: hasil readFile tanpa encoding, potongan data dari koneksi jaringan, keluaran crypto seperti hash dan bytes acak, hingga body permintaan HTTP. Buffer adalah mata uang data biner di seluruh runtime ini. Yang belum kita kuasai adalah aturan konversi antara Buffer dan string — berapa byte yang dipakai satu karakter, dan bagaimana encoding lain seperti base64 bekerja. Itulah pokok bahasan Chapter Encoding dan Konversi.