JavaScript ArrayBuffer, Array Binari
JavaScript offre ArrayBuffer e array tipizzati per gestire dati binari grezzi come file, immagini, audio e risposte di rete in modo efficiente.
Nella maggior parte dei casi JavaScript lavora con testo e JSON, ma nel momento in cui si gestiscono file, immagini, audio, WebSocket o risposte fetch, si ha a che fare con byte grezzi. JavaScript gestisce questi byte tramite ArrayBuffer e una famiglia di viste a array tipizzato. Questo capitolo spiega cos'è ogni componente, come si relazionano tra loro e quando utilizzarli — con esempi eseguibili che puoi verificare di persona.
Il modello mentale si divide in due livelli:
ArrayBuffer— un blocco di memoria grezza a lunghezza fissa. Sono solo byte; non è possibile leggerli o scriverli direttamente.- Viste (array tipizzati e
DataView) — oggetti che interpretano quei byte come numeri. Si legge e si scrive sempre tramite una vista.
Cos'è un ArrayBuffer?
Un ArrayBuffer è un contenitore generico a lunghezza fissa di dati binari grezzi — un blocco di memoria misurato in byte. Non ha alcuna nozione di "elementi" o tipi; sa solo quanti byte contiene. La sua dimensione viene impostata alla creazione e non può cambiare.
Per fare qualcosa con quei byte, si avvolge il buffer in una vista.
Array tipizzati: viste su un buffer
Un array tipizzato è una vista che interpreta il buffer come un array di numeri di un tipo fisso. L'indicizzazione, l'iterazione e le operazioni aritmetiche funzionano come in un array normale, ma i dati risiedono nella memoria grezza del buffer, il che rende gli array tipizzati compatti e veloci per grandi dataset numerici.
Il tipo di vista determina quanti byte occupa ogni elemento:
| Vista | Byte per elemento | Intervallo di valori |
|---|---|---|
Uint8Array | 1 | 0 … 255 |
Int8Array | 1 | -128 … 127 |
Uint16Array | 2 | 0 … 65535 |
Int16Array | 2 | -32768 … 32767 |
Uint32Array | 4 | 0 … 4294967295 |
Int32Array | 4 | -2³¹ … 2³¹-1 |
Float32Array | 4 | float a 32 bit |
Float64Array | 8 | float a 64 bit |
Uint8ClampedArray | 1 | 0 … 255 (con saturazione) |
Creare array tipizzati
È possibile costruire un array tipizzato a partire da una lunghezza, da un array esistente, o su un buffer esistente.
Più viste possono condividere un unico buffer
Questa è l'idea fondamentale: più viste possono insistere sullo stesso buffer. Scrivere tramite una vista modifica i byte, e ogni altra vista vede immediatamente la modifica. È così che si reinterpretano gli stessi byte in modi diversi.
Overflow e saturazione
Ogni elemento ha una larghezza fissa, quindi i valori fuori intervallo si avvolgono (vengono presi modulo la dimensione del tipo). Uint8ClampedArray è l'eccezione — satura a 0…255 invece di avvolgersi, il che è esattamente ciò di cui hanno bisogno i dati pixel del canvas.
Suddivisione e sotto-viste
slice() copia i dati in un nuovo buffer, mentre subarray() restituisce un'altra vista sulla stessa memoria — più economico, ma le scritture sono condivise.
DataView: tipi misti e ordine dei byte
Un array tipizzato impone un unico tipo e usa l'ordine dei byte nativo della piattaforma. DataView è l'alternativa di basso livello: consente di leggere e scrivere tipi diversi a qualsiasi offset di byte, e permette di scegliere esplicitamente l'ordine dei byte (endianness). Quel controllo è essenziale quando si analizzano formati di file e protocolli di rete, dove l'ordine dei byte è fissato da una specifica piuttosto che dalla CPU.
L'endianness è l'ordine in cui viene memorizzato un numero multi-byte: big-endian mette il byte più significativo per primo, little-endian lo mette per ultimo. Ogni metodo get/set su DataView accetta un flag littleEndian (il valore predefinito è big-endian).
Ecco lo stesso numero a 32 bit scritto in entrambi gli ordini di byte così puoi vedere la differenza:
Conversione tra byte e altri tipi
I dati binari raramente vivono da soli — si passa costantemente tra buffer, stringhe e oggetti di livello superiore.
Byte ↔ testo
Per convertire una stringa in byte (e viceversa), si usa TextEncoder / TextDecoder, che codificano in UTF-8. Vedi TextDecoder e TextEncoder per l'API completa.
Accedere al buffer sottostante
Ogni array tipizzato espone il proprio buffer, più byteOffset e byteLength che descrivono la regione che copre. È così che si passano i byte a un'API che richiede un ArrayBuffer.
Dove vengono effettivamente utilizzati gli array binari
- File. Leggere un file come
ArrayBufferpermette di esaminarne gli header, analizzare i formati o caricare byte grezzi. Vedi Blob e la File API. - Rete.
fetch(...).arrayBuffer()e i frame binari WebSocket restituiscono unArrayBufferda decodificare. - Canvas.
ctx.getImageData().dataè unUint8ClampedArraydi byte pixel RGBA che puoi leggere e riscrivere. - WebGL / Web Audio. I buffer di vertici e i campioni audio sono array tipizzati per motivi di prestazioni.
Esempio: leggere un file come ArrayBuffer
Nel browser, FileReader (o il più recente Blob.arrayBuffer()) legge il contenuto di un file in un buffer che poi si analizza con un DataView o un array tipizzato. Per una guida più completa vedi la File API.
function readBinaryFile(file) {
const reader = new FileReader();
reader.onload = () => {
const view = new DataView(reader.result);
console.log(view.getUint8(0)); // first byte, e.g. a format signature
};
reader.readAsArrayBuffer(file);
}
// Modern alternative:
// const buffer = await file.arrayBuffer();Buone pratiche
- Scegli il tipo più piccolo che sia sufficiente.
Uint8Arrayper flussi di byte,Float64Arrayper calcoli precisi — usare un tipo più ampio del necessario spreca memoria. - Usa
DataViewsolo quando hai bisogno di tipi misti o di un'endianness fissa. Per un array numerico uniforme, un array tipizzato è più semplice e veloce. - Riutilizza i buffer nei cicli intensivi per ridurre le allocazioni e il lavoro del garbage collector.
- Ricorda che le viste condividono la memoria — usa
slice()quando hai bisogno di una copia indipendente,subarray()quando vuoi intenzionalmente creare un alias.
Riepilogo
- Un
ArrayBufferè memoria grezza a lunghezza fissa; non la si tocca mai direttamente. - Gli array tipizzati visualizzano quella memoria come numeri di un tipo fisso e si comportano come array.
DataViewlegge e scrive tipi misti a offset arbitrari con endianness esplicita — ideale per analizzare protocolli e formati di file.- Le viste sullo stesso buffer condividono la memoria, quindi una scrittura tramite una vista è visibile attraverso tutte le altre.
- Questi tipi sono alla base di file, risposte di rete, pixel del canvas, audio e WebGL.