JavaScript - TypedArray & DataView

Overview

Estimated time: 35–45 minutes

Typed arrays provide efficient views over raw binary buffers for numbers and bytes. Use DataView for mixed-typed layouts and manual endianness control. Learn to encode/decode text and manipulate binary structures safely.

Learning Objectives

  • Create ArrayBuffer and view it with TypedArray types.
  • Use DataView to read/write different numeric types with chosen endianness.
  • Convert between strings and bytes with TextEncoder/TextDecoder.
  • Slice, copy, and share data between views correctly.

Prerequisites

ArrayBuffer and TypedArray

ArrayBuffer is a fixed-length raw binary buffer. Typed arrays are views that interpret bytes as specific numeric types.

// Create a 16-byte buffer and 32-bit integer view over it
const buf = new ArrayBuffer(16);
const u32 = new Uint32Array(buf); // 16 / 4 = 4 elements

u32[0] = 0x11223344;
u32[1] = 0xAABBCCDD;

// Another view on the same bytes
const u8 = new Uint8Array(buf); // 16 bytes
u8[0]; // first byte of the buffer (endianness affects how u32 maps to bytes)

// Copying vs sharing
const clone = u8.slice(); // copy (new buffer)
const view2 = new Uint8Array(buf, 4, 4); // view into bytes [4,8)

DataView for mixed types and endianness

DataView lets you read/write different numeric types at byte offsets and choose little/big endian per access.

const dv = new DataView(buf);
// Write values (little-endian true)
dv.setUint16(0, 0xABCD, true);  // bytes 0..1
dv.setInt32(2, -42, true);      // bytes 2..5

// Read values
dv.getUint16(0, true); // 43981 (0xABCD)
dv.getInt32(2, true);  // -42

From/To plain arrays and views

// Initialize from array (copies data)
const f32 = new Float32Array([1.0, 2.5, -3.75]);

// Copy between views
const out = new Uint8Array(6);
out.set(new Uint8Array(f32.buffer, 0, 6)); // copy first 6 bytes

// Subarray is a view window (no copy)
const window = f32.subarray(1); // shares buffer

Text encoding and decoding

// Encode JS string into UTF-8 bytes
const enc = new TextEncoder();
const bytes = enc.encode('Hello, 🌍'); // Uint8Array

// Decode UTF-8 bytes back to string
const dec = new TextDecoder('utf-8');
dec.decode(bytes); // 'Hello, 🌍'

Struct example

Define a simple binary record layout: u16 id, i8 flags, f32 value (little-endian).

function writeRecord(dv, offset, { id, flags, value }) {
  dv.setUint16(offset, id, true);      // 2 bytes
  dv.setInt8(offset + 2, flags);       // 1 byte
  dv.setFloat32(offset + 3, value, true); // 4 bytes
  return offset + 7;
}

function readRecord(dv, offset) {
  const id = dv.getUint16(offset, true);
  const flags = dv.getInt8(offset + 2);
  const value = dv.getFloat32(offset + 3, true);
  return { id, flags, value };
}

const buf2 = new ArrayBuffer(14);
const dv2 = new DataView(buf2);
writeRecord(dv2, 0, { id: 513, flags: -1, value: Math.PI });
writeRecord(dv2, 7, { id: 1024, flags: 3, value: -2.5 });
readRecord(dv2, 0); // { id: 513, flags: -1, value: 3.14159... }
readRecord(dv2, 7); // { id: 1024, flags: 3, value: -2.5 }

Common Pitfalls

  • Endianness: Typed arrays use platform endianness for multi-byte values; DataView lets you specify endian per access.
  • Bounds: Out-of-bounds reads/writes throw RangeError; ensure offsets and lengths are valid.
  • Sharing: Multiple views over the same buffer share bytes—mutations are visible across views.
  • Immutability: ArrayBuffer length is fixed; to grow, allocate a new buffer and copy or use resizeable ArrayBuffer in modern environments if available.

Try it

Run to write/read mixed types and encode/decode text: