Discover how Typed Arrays in JavaScript provide efficient handling of binary data and improve performance for numerical computations. 🚀
Understanding Typed Arrays 🎯
Typed Arrays are array-like objects that provide a mechanism for accessing raw binary data in a structured way. They're essential for WebGL, audio processing, and network communication.
Basic Concepts
ArrayBuffer
// Create a buffer of 16 bytes
const buffer = new ArrayBuffer(16);
console.log(buffer.byteLength); // 16
Different Types of Typed Arrays
// 8-bit integers
const int8Array = new Int8Array(4);
const uint8Array = new Uint8Array(4);
// 16-bit integers
const int16Array = new Int16Array(4);
const uint16Array = new Uint16Array(4);
// 32-bit integers
const int32Array = new Int32Array(4);
const uint32Array = new Uint32Array(4);
// Floating point numbers
const float32Array = new Float32Array(4);
const float64Array = new Float64Array(4);
Working with Typed Arrays 🔄
Basic Operations
// Creating and initializing
const numbers = new Int32Array([1, 2, 3, 4]);
// Setting values
numbers[0] = 10;
// Getting values
console.log(numbers[0]); // 10
// Length
console.log(numbers.length); // 4
// Byte length
console.log(numbers.byteLength); // 16 (4 * 4 bytes)
Array Methods
const array = new Int32Array([1, 2, 3, 4, 5]);
// Map
const doubled = array.map(x => x * 2);
// Filter
const evenNumbers = array.filter(x => x % 2 === 0);
// Reduce
const sum = array.reduce((a, b) => a + b, 0);
// forEach
array.forEach(x => console.log(x));
Real-World Applications 💡
1. WebGL Graphics
class WebGLRenderer {
constructor(canvas) {
this.gl = canvas.getContext('webgl');
// Vertex positions
const vertices = new Float32Array([
-1.0, -1.0,
1.0, -1.0,
0.0, 1.0
]);
// Create buffer
const buffer = this.gl.createBuffer();
this.gl.bindBuffer(this.gl.ARRAY_BUFFER, buffer);
this.gl.bufferData(
this.gl.ARRAY_BUFFER,
vertices,
this.gl.STATIC_DRAW
);
}
}
2. Audio Processing
class AudioProcessor {
constructor(audioContext) {
this.context = audioContext;
this.buffer = this.context.createBuffer(
2, // channels
this.context.sampleRate * 1, // 1 second
this.context.sampleRate
);
}
generateSineWave(frequency) {
const channelData = this.buffer.getChannelData(0);
const sampleRate = this.context.sampleRate;
for (let i = 0; i < channelData.length; i++) {
channelData[i] = Math.sin(
2 * Math.PI * frequency * i / sampleRate
);
}
}
}
3. Network Communication
class BinaryProtocol {
static encode(message) {
const encoder = new TextEncoder();
const messageBytes = encoder.encode(message);
const buffer = new ArrayBuffer(messageBytes.length + 4);
const view = new DataView(buffer);
// Write length prefix
view.setUint32(0, messageBytes.length);
// Write message
new Uint8Array(buffer, 4).set(messageBytes);
return buffer;
}
static decode(buffer) {
const view = new DataView(buffer);
const length = view.getUint32(0);
const messageBytes = new Uint8Array(
buffer,
4,
length
);
const decoder = new TextDecoder();
return decoder.decode(messageBytes);
}
}
Performance Optimization 🚀
1. Memory Management
class MemoryPool {
constructor(size) {
this.buffer = new ArrayBuffer(size);
this.used = 0;
}
allocate(bytes) {
if (this.used + bytes > this.buffer.byteLength) {
throw new Error('Out of memory');
}
const view = new Uint8Array(
this.buffer,
this.used,
bytes
);
this.used += bytes;
return view;
}
reset() {
this.used = 0;
}
}
2. Data Transfer
class DataTransfer {
static transfer(sourceArray, targetArray) {
if (sourceArray.byteLength !== targetArray.byteLength) {
throw new Error('Arrays must have same byte length');
}
// Fast copy between typed arrays
targetArray.set(sourceArray);
}
static copy(array) {
return new array.constructor(array);
}
}
Advanced Features 📚
1. DataView for Endianness
class BinaryReader {
constructor(buffer) {
this.view = new DataView(buffer);
this.offset = 0;
}
readInt32() {
const value = this.view.getInt32(this.offset, true); // little-endian
this.offset += 4;
return value;
}
readFloat64() {
const value = this.view.getFloat64(this.offset, true);
this.offset += 8;
return value;
}
}
2. SharedArrayBuffer for Threading
// Main thread
const shared = new SharedArrayBuffer(4);
const view = new Int32Array(shared);
worker.postMessage({ shared });
// Worker thread
self.onmessage = function(e) {
const view = new Int32Array(e.data.shared);
Atomics.add(view, 0, 1);
};
Best Practices 📝
1. Memory Efficiency
// ❌ Inefficient
const arrays = [];
for (let i = 0; i < 1000; i++) {
arrays.push(new Float64Array(1000));
}
// ✅ Efficient
const buffer = new ArrayBuffer(1000 * 1000 * 8);
const views = [];
for (let i = 0; i < 1000; i++) {
views.push(new Float64Array(
buffer,
i * 1000 * 8,
1000
));
}
2. Type Selection
// Choose appropriate type for data
const pixels = new Uint8Array(width * height * 4); // RGBA
const audio = new Float32Array(sampleRate * duration);
const positions = new Float64Array(points * 3); // XYZ
Common Patterns 🔄
1. Buffer Pooling
class BufferPool {
constructor(bufferSize, poolSize) {
this.pool = Array.from(
{ length: poolSize },
() => new ArrayBuffer(bufferSize)
);
}
acquire() {
return this.pool.pop() ||
new ArrayBuffer(this.bufferSize);
}
release(buffer) {
if (this.pool.length < this.poolSize) {
this.pool.push(buffer);
}
}
}
2. Streaming Data
class BinaryStream {
constructor(capacity = 1024) {
this.buffer = new ArrayBuffer(capacity);
this.view = new DataView(this.buffer);
this.position = 0;
}
write(value, type = 'Int32') {
const method = `set${type}`;
this.view[method](this.position, value, true);
this.position += this.typeSize(type);
}
typeSize(type) {
const sizes = {
Int8: 1,
Int16: 2,
Int32: 4,
Float32: 4,
Float64: 8
};
return sizes[type] || 0;
}
}
Additional Resources
Typed Arrays are essential for modern web applications that deal with binary data, providing efficient and structured ways to handle raw data in JavaScript. Understanding their proper use can significantly improve application performance in specific scenarios.