JavaScript Typed Arrays: A Comprehensive Tutorial

Typed arrays in JavaScript provide a way to work with binary data using arrays of fixed types and sizes. They are particularly useful when interacting with raw binary data from external sources such as files, network operations, or when performing high-performance tasks like real-time audio/video processing, graphics, and data visualization.

Typed arrays consist of buffer objects and view objects:

ArrayBuffer: Represents a fixed-length block of memory.
Typed array views: Allow you to interpret and manipulate the binary data in an ArrayBuffer using specific types like Int8, Uint8, Int16, Float32, etc.
This tutorial will explore how to create and work with typed arrays using practical code examples.

1. Understanding ArrayBuffer

An ArrayBuffer is a generic, fixed-length binary data buffer. It represents a chunk of memory that can be accessed and manipulated using typed arrays.

Example 1: Creating an ArrayBuffer

// Create an ArrayBuffer of 16 bytes
const buffer = new ArrayBuffer(16);
console.log(buffer.byteLength); // Output: 16

Explanation:
new ArrayBuffer(16) creates a buffer with 16 bytes.
The byteLength property shows the size of the buffer in bytes.

2. Creating Typed Arrays

Typed arrays are views on an ArrayBuffer that allow you to read and write binary data in a specific format (e.g., Int8, Uint16, Float32). Typed arrays include:

Int8Array, Uint8Array, Uint8ClampedArray: 8-bit signed, unsigned, and clamped integers.
Int16Array, Uint16Array: 16-bit signed and unsigned integers.
Int32Array, Uint32Array: 32-bit signed and unsigned integers.
Float32Array, Float64Array: 32-bit and 64-bit floating-point numbers.

Example 2: Creating a Typed Array (Int8Array)

// Create an ArrayBuffer of 8 bytes
const buffer = new ArrayBuffer(8);

// Create a view of the buffer as an Int8Array
const int8View = new Int8Array(buffer);

console.log(int8View.length); // Output: 8
console.log(int8View); // Output: Int8Array(8) [0, 0, 0, 0, 0, 0, 0, 0]

// Set values in the typed array
int8View[0] = 127;
int8View[1] = -128;
console.log(int8View); // Output: Int8Array(8) [127, -128, 0, 0, 0, 0, 0, 0]

Explanation:

We create an ArrayBuffer of 8 bytes.
An Int8Array view allows us to interpret each byte of the buffer as an 8-bit signed integer.
The length of the typed array corresponds to the number of elements it can hold based on the buffer size (1 byte per element for Int8Array).

3. Typed Array Views

Typed arrays provide various views to interpret an ArrayBuffer in different formats:

Int8Array: 8-bit signed integers.
Uint8Array: 8-bit unsigned integers.
Int16Array: 16-bit signed integers.
Uint16Array: 16-bit unsigned integers.
Int32Array: 32-bit signed integers.
Uint32Array: 32-bit unsigned integers.
Float32Array: 32-bit floating-point numbers.
Float64Array: 64-bit floating-point numbers.

Example 3: Using Different Typed Array Views on the Same Buffer

const buffer = new ArrayBuffer(4);

// Create different typed array views
const int8View = new Int8Array(buffer);
const int16View = new Int16Array(buffer);
const float32View = new Float32Array(buffer);

// Set a value using the Int8Array view
int8View[0] = 42;

console.log(int8View);      // Output: Int8Array(4) [42, 0, 0, 0]
console.log(int16View);     // Output: Int16Array(2) [42, 0]
console.log(float32View);   // Output: Float32Array(1) [5.885453947183122e-44]

Explanation:

We create an ArrayBuffer of 4 bytes and create different typed array views (Int8Array, Int16Array, Float32Array) on the same buffer.
Modifying the buffer using one view affects the other views, as they share the same memory.

4. Typed Arrays with Initial Values

Typed arrays can be initialized with an array of values directly. In this case, a new ArrayBuffer is created internally.

Example 4: Initializing a Typed Array with Values

// Create a Uint8Array with initial values
const uint8Array = new Uint8Array([10, 20, 30, 40, 50]);

console.log(uint8Array); // Output: Uint8Array(5) [10, 20, 30, 40, 50]

// Accessing elements
console.log(uint8Array[2]); // Output: 30

// Modifying elements
uint8Array[2] = 99;
console.log(uint8Array); // Output: Uint8Array(5) [10, 20, 99, 40, 50]

Explanation:
We create a Uint8Array with initial values [10, 20, 30, 40, 50].
Elements can be accessed and modified using array index notation.

5. Clamped Arrays with Uint8ClampedArray

Uint8ClampedArray is a special type of typed array where values are clamped to the range [0, 255]. If you set a value less than 0, it will be clamped to 0. If you set a value greater than 255, it will be clamped to 255.

Example 5: Using Uint8ClampedArray

const clampedArray = new Uint8ClampedArray(4);

// Set values in the clamped array
clampedArray[0] = 100;
clampedArray[1] = -50;  // Will be clamped to 0
clampedArray[2] = 300;  // Will be clamped to 255
clampedArray[3] = 200;

console.log(clampedArray); // Output: Uint8ClampedArray(4) [100, 0, 255, 200]

Explanation:
Uint8ClampedArray clamps values to the range of 0 to 255.
Setting a value outside this range automatically adjusts it to the nearest valid value.

6. Copying and Slicing Typed Arrays

Typed arrays support set() to copy values from another array and subarray() to create a new view on the existing buffer.

Example 6: Copying and Slicing Typed Arrays

const srcArray = new Uint8Array([10, 20, 30, 40, 50]);
const destArray = new Uint8Array(5);

// Copy values from `srcArray` to `destArray`
destArray.set(srcArray);

console.log(destArray); // Output: Uint8Array(5) [10, 20, 30, 40, 50]

// Creating a subarray (view on the original buffer)
const subArray = srcArray.subarray(1, 4);
console.log(subArray); // Output: Uint8Array(3) [20, 30, 40]

Explanation:
The set() method copies values from srcArray to destArray.
The subarray() method creates a new view on the buffer, starting at index 1 and ending before index 4.

7. Endianness and DataView

Binary data can be stored in different byte orders (endianness). The DataView object provides more control over reading and writing data with specific endianness.

Example 7: Using DataView to Handle Endianness

const buffer = new ArrayBuffer(4);
const dataView = new DataView(buffer);

// Set a 16-bit value at byte offset 0, using little-endian
dataView.setUint16(0, 0x1234, true);

console.log(dataView.getUint16(0, true));  // Output: 4660 (0x1234 in decimal)
console.log(dataView.getUint16(0, false)); // Output: 13330 (byte-swapped)

Explanation:

DataView provides methods to set and get values with control over endianness.
The true or false parameter specifies whether to use little-endian or big-endian byte order.

Conclusion

Typed arrays in JavaScript are a powerful way to handle binary data and perform high-performance operations. They are particularly useful in scenarios like graphics processing, working with audio and video data, interacting with Web APIs, and scientific computing. By using ArrayBuffer and typed array views, you can manage raw binary data efficiently.

Key Points:

ArrayBuffer: Represents a fixed-length block of memory.
Typed Arrays: Views (Int8Array, Uint16Array, Float32Array, etc.) to interpret and manipulate binary data.
Uint8ClampedArray: Automatically clamps values to the range [0, 255].
DataView: Provides low-level access to ArrayBuffer data with specific endianness.

Experiment with different typed arrays and DataView to understand their behavior and how to use them for binary data manipulation in JavaScript applications!

Related posts

JavaScript User-Defined Iterators Tutorial with Examples

JavaScript Predicate Functions Tutorial with Examples

JavaScript Template Literals Tutorial with Examples