Typed Arrays and ArrayBuffer
Binary data in JS: ArrayBuffer, views (Uint8Array, DataView), endianness, slices vs subarray, and worker transfer.
- javascript
- typed-arrays
JS arrays are flexible and heavy. Binary protocols, WebGL, audio, WASM, and file I/O need raw bytes. ArrayBuffer is a fixed-length chunk of memory; typed array views and DataView read/write that memory with types.
const buf = new ArrayBuffer(16); // 16 bytes
const bytes = new Uint8Array(buf);
bytes[0] = 255;
bytes[1] = 1;
const words = new Uint32Array(buf);
// words and bytes share the same memory — reinterpret cast
Common views
| Type | Bytes/element | Range (approx) |
|---|---|---|
Uint8Array |
1 | 0…255 |
Int8Array |
1 | -128…127 |
Uint16Array / Int16Array |
2 | … |
Uint32Array / Int32Array |
4 | … |
Float32Array / Float64Array |
4 / 8 | IEEE floats |
BigInt64Array / BigUint64Array |
8 | bigint elements |
const f32 = new Float32Array([1.5, 2.5]);
f32.buffer; // underlying ArrayBuffer
f32.byteOffset;
f32.byteLength;
DataView for mixed layouts / endianness
Protocols interleave fields; endianness matters on the wire.
const buf = new ArrayBuffer(8);
const view = new DataView(buf);
view.setUint16(0, 0x1234, false); // big-endian
view.setFloat32(2, 3.14, true); // little-endian at offset 2
view.getUint16(0, false); // 0x1234
Typed arrays use the platform endianness for multi-byte elements; DataView makes endian explicit.
slice vs subarray
const u8 = new Uint8Array([1, 2, 3, 4]);
const sub = u8.subarray(1, 3); // view same buffer [2,3]
sub[0] = 99;
u8[1]; // 99
const copy = u8.slice(1, 3); // new buffer copy
subarray is cheap sharing; slice allocates.
Detach / transfer
const buf = new ArrayBuffer(1024);
worker.postMessage(buf, [buf]); // transfer
// buf.byteLength === 0 in sender — detached
structuredClone(buf, { transfer: [buf] }) same idea. Zero-copy moves for large media.
Text vs binary
const u8 = new TextEncoder().encode('hi'); // Uint8Array
const s = new TextDecoder().decode(u8);
Fetch: await res.arrayBuffer() / await res.bytes() (newer) for binary responses.
When to use
- Parsing binary file formats
- WebSocket
ArrayBuffermessages - Canvas pixel buffers
- Crypto subtle APIs
- Interop with WASM memory
Don’t replace normal JS arrays for lists of objects — typed arrays are for homogeneous numbers/bytes.
Interview answer (out loud)
“ArrayBuffer is raw memory; typed arrays are typed views over it; DataView reads mixed fields with explicit endianness. subarray shares memory, slice copies. Buffers can be transferred to workers for zero-copy. I use TextEncoder/Decoder for UTF-8 at the edges.”
Alignment
Multi-byte views need offsets aligned to element size when using typed array constructors on a buffer with byteOffset; DataView is more forgiving for unaligned protocol fields.
// May throw if offset not aligned for Uint32Array
// new Uint32Array(buf, 1, 1);
Cloning views
const a = new Uint8Array([1, 2, 3]);
const b = new Uint8Array(a); // copy
const c = a.slice(); // copy
new Uint8Array(a.buffer) shares memory — know which constructor args copy.
WASM memory
WebAssembly linear memory is an ArrayBuffer (or shared variant). Typed arrays are how JS reads WASM heaps — bounds checks still matter for safety at the app layer.
Further reading
Related
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.