JavaScript ArrayBuffer, Binäre Arrays
JavaScript bietet mit ArrayBuffer und typisierten Arrays leistungsstarke Werkzeuge für die Verarbeitung von Binärdaten in modernen Webanwendungen.
JavaScript arbeitet meistens mit Text und JSON, aber sobald man Dateien, Bilder, Audio, WebSockets oder fetch-Antworten verarbeitet, hat man es mit rohen Bytes zu tun. JavaScript verwaltet diese Bytes über ArrayBuffer und eine Familie von typisierten Array-Ansichten. Dieses Kapitel erklärt, was jede Komponente ist, wie sie zusammenhängen und wann man sie einsetzen sollte — mit ausführbaren Beispielen, die man selbst überprüfen kann.
Das mentale Modell besteht aus zwei Ebenen:
ArrayBuffer— ein Block mit fester Länge aus rohem Speicher. Er besteht nur aus Bytes; man kann ihn nicht direkt lesen oder schreiben.- Ansichten (typisierte Arrays und
DataView) — Objekte, die diese Bytes als Zahlen interpretieren. Lesen und Schreiben erfolgt immer über eine Ansicht.
Was ist ein ArrayBuffer?
Ein ArrayBuffer ist ein generischer Container mit fester Länge für rohe Binärdaten — ein Speicherblock, der in Bytes gemessen wird. Er kennt keine „Elemente" oder Typen; er weiß nur, wie viele Bytes er enthält. Seine Größe wird bei der Erstellung festgelegt und kann nicht geändert werden.
Um tatsächlich etwas mit diesen Bytes zu tun, muss man den Buffer in eine Ansicht einwickeln.
Typisierte Arrays: Ansichten über einen Buffer
Ein typisiertes Array ist eine Ansicht, die den Buffer als Array von Zahlen eines festen Typs interpretiert. Indizierung, Iteration und Arithmetik funktionieren genau wie bei einem normalen array, aber die Daten befinden sich im rohen Speicher des Buffers, was typisierte Arrays kompakt und schnell für große numerische Datensätze macht.
Der Ansichtstyp bestimmt, wie viele Bytes jedes Element belegt:
| Ansicht | Bytes pro Element | Wertebereich |
|---|---|---|
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 | 32-Bit-Gleitkomma |
Float64Array | 8 | 64-Bit-Gleitkomma |
Uint8ClampedArray | 1 | 0 … 255 (begrenzt) |
Typisierte Arrays erstellen
Ein typisiertes Array kann aus einer Länge, einem vorhandenen array oder über einen bestehenden Buffer erstellt werden.
Mehrere Ansichten können einen Buffer teilen
Das ist der Kerngedanke: Mehrere Ansichten können auf demselben Buffer liegen. Schreibt man durch eine Ansicht, ändern sich die Bytes, und jede andere Ansicht sieht die Änderung sofort. So kann man dieselben Bytes auf unterschiedliche Weisen interpretieren.
Überlauf und Begrenzung
Jedes Element hat eine feste Breite, daher werden Werte außerhalb des Bereichs umgewickelt (Modulo der Typgröße). Uint8ClampedArray ist die Ausnahme — es begrenzt auf 0…255 statt umzuwickeln, was genau das ist, was Canvas-Pixeldaten benötigen.
Slicing und Teilansichten
slice() kopiert in einen brandneuen Buffer, während subarray() eine weitere Ansicht über denselben Speicher zurückgibt — günstiger, aber Schreiboperationen sind gemeinsam.
DataView: gemischte Typen und Byte-Reihenfolge
Ein typisiertes Array erzwingt einen einzigen Typ und verwendet die native Byte-Reihenfolge der Plattform. DataView ist die Low-Level-Alternative: Es ermöglicht das Lesen und Schreiben verschiedener Typen an beliebigen Byte-Offsets und lässt einen die Byte-Reihenfolge (Endianness) explizit wählen. Diese Kontrolle ist unerlässlich beim Parsen von Dateiformaten und Netzwerkprotokollen, bei denen die Byte-Reihenfolge durch eine Spezifikation und nicht durch die CPU festgelegt ist.
Endianness ist die Reihenfolge, in der eine mehrbytigen Zahl gespeichert wird: Big-Endian stellt das höchstwertige Byte zuerst, Little-Endian stellt es zuletzt. Jede get/set-Methode von DataView nimmt ein littleEndian-Flag entgegen (Standard ist Big-Endian).
Hier wird dieselbe 32-Bit-Zahl in beiden Byte-Reihenfolgen geschrieben, damit man den Unterschied sehen kann:
Konvertierung zwischen Bytes und anderen Typen
Binärdaten leben selten allein — man wechselt ständig zwischen Buffern, Strings und Objekten auf höherer Ebene.
Bytes ↔ Text
Um einen string in Bytes (und zurück) umzuwandeln, verwendet man TextEncoder / TextDecoder, die als UTF-8 kodieren. Siehe TextDecoder und TextEncoder für die vollständige API.
Zugriff auf den zugrunde liegenden Buffer
Jedes typisierte Array gibt seinen buffer sowie byteOffset und byteLength preis, die den abgedeckten Bereich beschreiben. So übergibt man Bytes an eine API, die einen ArrayBuffer erwartet.
Wo binäre Arrays tatsächlich eingesetzt werden
- Dateien. Das Lesen einer Datei als
ArrayBufferermöglicht die Untersuchung von Headern, das Parsen von Formaten oder das Hochladen roher Bytes. Siehe Blob und die File API. - Netzwerk.
fetch(...).arrayBuffer()und WebSocket-Binärframes liefern einenArrayBufferzum Dekodieren. - Canvas.
ctx.getImageData().dataist einUint8ClampedArrayaus RGBA-Pixelbytes, die man lesen und neu schreiben kann. - WebGL / Web Audio. Vertex-Buffer und Audiosamples sind typisierte Arrays für die Performance.
Beispiel: Eine Datei als ArrayBuffer lesen
Im Browser liest FileReader (oder das neuere Blob.arrayBuffer()) Dateiinhalte in einen Buffer, den man dann mit einem DataView oder typisierten array parst. Einen ausführlicheren Leitfaden bietet die 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();Best Practices
- Den kleinsten passenden Typ wählen.
Uint8Arrayfür Byte-Streams,Float64Arrayfür präzise Mathematik — ein breiterer Typ als nötig verschwendet Speicher. DataViewnur verwenden, wenn gemischte Typen oder eine feste Endianness benötigt wird. Für ein gleichartiges numerisches array ist ein typisiertes Array einfacher und schneller.- Buffer in heißen Schleifen wiederverwenden, um Allokation und Garbage-Collection-Overhead zu reduzieren.
- Daran denken, dass Ansichten Speicher teilen —
slice()verwenden, wenn eine unabhängige Kopie benötigt wird,subarray(), wenn man bewusst einen Alias erstellen möchte.
Zusammenfassung
- Ein
ArrayBufferist roher, unveränderlicher Speicher mit fester Länge; man greift nie direkt auf ihn zu. - Typisierte Arrays betrachten diesen Speicher als Zahlen eines einzigen festen Typs und verhalten sich wie arrays.
DataViewliest und schreibt gemischte Typen an beliebigen Offsets mit expliziter Endianness — ideal für das Parsen von Protokollen und Dateiformaten.- Ansichten über denselben Buffer teilen den Speicher, sodass ein Schreibvorgang durch eine Ansicht für alle sichtbar ist.
- Diese Typen sind die Basis für Dateien, Netzwerkantworten, Canvas-Pixel, Audio und WebGL.