ArrayBuffer i TypeScript

ArrayBuffer i TypeScript

Denne artikel forklarer ArrayBuffer i TypeScript.

Vi forklarer ArrayBuffer i TypeScript trin for trin, fra det grundlæggende til praktiske teknikker.

YouTube Video

ArrayBuffer i TypeScript

ArrayBuffer er et indbygget objekt, der repræsenterer et “råt hukommelsesområde” til binære data. Det repræsenterer en rå buffer med fast længde, hvor TypedArray eller DataView bruges til at læse og skrive.

Grundlæggende koncepter og egenskaber for ArrayBuffer

ArrayBuffer er en byte-sekvens med fast længde. Du angiver størrelsen i bytes, når du opretter den, og længden kan ikke ændres bagefter.

1// Create a new ArrayBuffer of 16 bytes.
2const buf = new ArrayBuffer(16);
3console.log(buf.byteLength); // 16
  • Denne kode opretter en tom buffer på 16 bytes. Du kan kontrollere størrelsen med byteLength.

TypedArray og DataView — Visninger til at manipulere buffere

ArrayBuffer har ikke evnen til at læse eller skrive data. Derfor udføres faktiske operationer gennem TypedArray eller DataView, hvor du angiver typer som heltal eller flydende tal samt endianhed, når du tilgår data.

1// Create a buffer and a Uint8Array view over it. Then write bytes and read them.
2const buffer = new ArrayBuffer(8);
3const u8 = new Uint8Array(buffer);
4
5u8[0] = 0x41; // 'A'
6u8[1] = 0x42; // 'B'
7console.log(u8); // Uint8Array(8) [ 65, 66, 0, 0, 0, 0, 0, 0 ]
  • Uint8Array er en byte-orienteret array-visning, som kan tilgås som et almindeligt array. Hvis du placerer flere visninger på den samme ArrayBuffer, deler de samme hukommelse til læsning og skrivning.

DataView: Læsning og skrivning ved vilkårlige grænser og endianness

DataView er nyttig til detaljeret læsning/skrift byte for byte, og lader dig angive little eller big endian.

 1// Using DataView to write/read multi-byte values with endianness control.
 2const buf2 = new ArrayBuffer(8);
 3const view = new DataView(buf2);
 4
 5// Write a 32-bit integer (little-endian)
 6view.setInt32(0, 0x12345678, true);
 7
 8// Read it back as little-endian and big-endian
 9const little = view.getInt32(0, true);
10const big = view.getInt32(0, false);
11console.log(little.toString(16)); // "12345678"
12console.log(big.toString(16));    // "78563412"
  • DataView gør det muligt at læse og skrive værdier ved at angive offset i hukommelsen, hvilket gør det velegnet til at implementere protokoller, der kræver håndtering af netværks-byterækkefølge.

Konvertering mellem strenge og ArrayBuffer (TextEncoder / TextDecoder)

For at konvertere tekst til binært og omvendt, brug TextEncoder og TextDecoder.

 1// Convert string -> ArrayBuffer and back using TextEncoder/TextDecoder.
 2const encoder = new TextEncoder();
 3const decoder = new TextDecoder();
 4
 5// Unicode escape sequences
 6const str = "\u3053\u3093\u306B\u3061\u306F";
 7const encoded = encoder.encode(str); // Uint8Array
 8console.log(encoded); // Uint8Array([...])
 9
10// If you need an ArrayBuffer specifically:
11const ab = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
12console.log(ab.byteLength); // bytes length of encoded text
13
14// Decode back
15const decoded = decoder.decode(encoded);
16console.log(decoded);
  • TextEncoder.encode returnerer en Uint8Array, så hvis du har brug for et ArrayBuffer, skal du referere til dens .buffer-egenskab. Med slice-metoden kan du udtrække de nødvendige data ved at angive offset og længde.

Udskæring og kopiering af ArrayBuffer

slice-metoden returnerer et nyt ArrayBuffer. ArrayBuffer kan ikke ændre størrelse; hvis ændring af størrelse er nødvendig, skal du oprette en ny buffer og kopiere dataene ind i den.

 1// Slice an ArrayBuffer and copy to a new sized buffer.
 2const original = new Uint8Array([1,2,3,4,5]).buffer;
 3const part = original.slice(1, 4); // bytes 1..3
 4console.log(new Uint8Array(part)); // Uint8Array [ 2, 3, 4 ]
 5
 6// Resize: create a new buffer and copy existing content
 7const larger = new ArrayBuffer(10);
 8const target = new Uint8Array(larger);
 9target.set(new Uint8Array(original), 0);
10console.log(target); // first bytes filled with original data
  • Da slice-metoden returnerer et nyt ArrayBuffer, kan du, hvis du prioriterer effektivitet eller deling af referencer, bruge subarray-metoden på en TypedArray for at referere til den samme buffer.

Forskel mellem TypedArray.subarray og kopiering

TypedArray's subarray returnerer en ny visning, der refererer til den samme ArrayBuffer.

1// subarray shares the same underlying buffer; modifying one affects the other.
2const arr = new Uint8Array([10,20,30,40]);
3const viewSub = arr.subarray(1,3); // shares memory
4viewSub[0] = 99;
5console.log(arr); // Uint8Array [10, 99, 30, 40]
  • Delte visninger kan undgå omkostningerne ved kopiering, men da de refererer til den samme buffer, skal du være opmærksom på bivirkninger.

Sammenkædning af buffers (Kombinering af flere ArrayBuffers)

Da ArrayBuffer har fast længde, skal du oprette en ny ArrayBuffer og kopiere dataene for at kombinere flere buffere.

 1// Concatenate multiple ArrayBuffers
 2function concatBuffers(buffers: ArrayBuffer[]): ArrayBuffer {
 3  const total = buffers.reduce((sum, b) => sum + b.byteLength, 0);
 4  const result = new Uint8Array(total);
 5  let offset = 0;
 6  for (const b of buffers) {
 7    const u8 = new Uint8Array(b);
 8    result.set(u8, offset);
 9    offset += u8.length;
10  }
11  return result.buffer;
12}
13
14const a = new Uint8Array([1,2]).buffer;
15const b = new Uint8Array([3,4,5]).buffer;
16const c = concatBuffers([a, b]);
17console.log(new Uint8Array(c)); // [1,2,3,4,5]
  • Hvis du ofte sammenkæder store datamængder, er det mere effektivt at forudberegne den samlede størrelse og kun allokere en ny buffer én gang, som i denne kode.

Overførsel af ArrayBuffer til en Worker (transferable)

I browserens postMessage kan du overføre et ArrayBuffer som et "transferable" objekt. Ejerskab kan overføres uden kopiering, hvilket undgår omkostningen ved kopiering.

1// Example: posting an ArrayBuffer to a Worker as a transferable object (browser)
2const worker = new Worker('worker.js');
3const bufferToSend = new Uint8Array([1,2,3,4]).buffer;
4
5// Transfer ownership to the worker (main thread no longer owns it)
6worker.postMessage(bufferToSend, [bufferToSend]);
7
8// After transfer, bufferToSend.byteLength === 0 in many browsers (detached)
9console.log(bufferToSend.byteLength); // may be 0
  • Ved at angive de objekter, der skal overføres, i et array som det andet argument til postMessage, kan du overføre ejerskab af "transferable" objekter som ArrayBuffer uden at kopiere dem.
  • Efter overførsel bliver bufferen "detached" på den oprindelige side og kan ikke tilgås. Brug af SharedArrayBuffer tillader samtidig adgang fra flere tråde, men dets brug indebærer sikkerhedskrav og miljøbegrænsninger.

Håndtering i Node.js (Konvertering med Buffer)

I Node.js kan du konvertere mellem Node.js-typen Buffer og ArrayBuffer. Dette er nyttigt, når du vil understøtte både browsere og Node.js med TypeScript.

 1// Convert ArrayBuffer <-> Node.js Buffer
 2// In Node.js environment:
 3const ab = new Uint8Array([10,20,30]).buffer;
 4const nodeBuffer = Buffer.from(ab); // ArrayBuffer -> Buffer
 5console.log(nodeBuffer); // <Buffer 0a 14 1e>
 6
 7const backToAb = nodeBuffer.buffer.slice(
 8    nodeBuffer.byteOffset,
 9    nodeBuffer.byteOffset + nodeBuffer.byteLength
10);
11console.log(new Uint8Array(backToAb)); // Uint8Array [10,20,30]
  • Buffer.from(arrayBuffer) i Node.js laver normalt en kopi, men der findes tilfælde, hvor reference deles, så vær opmærksom på offset.

Ydelsesmæssige hensyn og bedste praksis

For at optimere ydelsen og håndtere ArrayBuffer effektivt er der flere vigtige punkter at være opmærksom på. Nedenfor oplister og forklarer vi praktiske bedste praksis.

  • Reducer antallet af kopier Når du håndterer store binære data, bør du bruge subarray (delt visning) eller transferables for at reducere kopiering.

  • Allokér store buffere på én gang Gentagen allokering af små buffere øger overhead. Hvis det er muligt, så tildel en stor buffer på én gang og brug dele af den efter behov.

  • Angiv endianness eksplicit Når du håndterer multibyte-værdier, skal du bruge DataView og eksplicit angive endianness. Big-endian er ofte standard for netværksprotokoller.

Almindelige anvendelsestilfælde

ArrayBuffer bruges bredt både i browsere og Node.js.

  1. Parsing og opbygning af binære protokoller (behandling af header-information med DataView)
  2. Mediebehandling som billed- og lyddata (fetch(...).then(res => res.arrayBuffer()))
  3. Delt hukommelse for WebAssembly (Wasm-hukommelse arbejder med ArrayBuffer)
  4. Aflastning af tung behandling til Workers (overførsel af ArrayBuffer som transferable)

Følgende kode er et eksempel på indhentning og analyse af binære data.

1// Example: fetch binary data in browser and inspect first bytes
2async function fetchAndInspect(url: string) {
3  const resp = await fetch(url);
4  const ab = await resp.arrayBuffer();
5  const u8 = new Uint8Array(ab, 0, Math.min(16, ab.byteLength));
6  console.log('first bytes:', u8);
7}
  • Denne kode henter binære data fra en hvilken som helst URL i browseren og viser de første par bytes. Koden indlæser data hentet via fetch-API'en som en ArrayBuffer og undersøger de første 16 bytes med en Uint8Array.

Sammendrag

ArrayBuffer er en rå hukommelsesrepræsentation, der gør det muligt at udføre effektive binære operationer via TypedArray og DataView. Ved at designe til at undgå unødvendig kopiering og eksplicit angive endianness kan du opnå sikker og højtydende binær behandling.

Du kan følge med i ovenstående artikel ved hjælp af Visual Studio Code på vores YouTube-kanal. Husk også at tjekke YouTube-kanalen.

YouTube Video