TypeScript 中的 TypedArray
本文解釋了 TypeScript 中的 TypedArray。
我們將說明 TypeScript 中的 TypedArray,並提供實用的範例。
YouTube Video
TypeScript 中的 TypedArray
TypedArray 是一種高效處理二進位資料的機制。它特別適合用於大型圖像資料、網路位元組流,以及 WebGL 的數值陣列等底層二進位操作。
如何建立 ArrayBuffer
ArrayBuffer 代表一段固定長度的位元組區域。首先,建立一個緩衝區並檢查其大小與位元組長度。
1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
- 此程式碼會建立一段 16 位元組的空白區域。
ArrayBuffer本身沒有讀寫功能,需透過TypedArray或DataView來存取。
TypedArray 的類型
TypedArray 有多種類型,例如:。這些類型根據它們處理的數據類型和大小而有所不同。
| TypedArray | 數據類型 | 位元大小 |
|---|---|---|
Int8Array |
8 位元整數 | 8 位元 |
Uint8Array |
無符號 8 位元整數 | 8 位元 |
Uint8ClampedArray |
限幅無符號 8 位元整數 | 8 位元 |
Int16Array |
16 位元整數 | 16 位元 |
Uint16Array |
無符號 16 位元整數 | 16 位元 |
Int32Array |
32 位元整數 | 32 位元 |
Uint32Array |
無符號 32 位元整數 | 32 位元 |
Float32Array |
32 位元浮點數 | 32 位元 |
Float64Array |
64 位元浮點數 | 64 位元 |
基本的 TypedArray(如 Uint8Array、Int16Array、Float32Array 等)
TypedArray 會在 ArrayBuffer 上建立「有型別的視圖」。以下是建立及使用常見 TypedArray 的範例。
1// Create a buffer and different typed views over it
2const buf = new ArrayBuffer(8); // 8 bytes
3
4// Create views
5const u8 = new Uint8Array(buf); // 8 x uint8
6const i16 = new Int16Array(buf); // 4 x int16 (since each int16 is 2 bytes)
7const f32 = new Float32Array(buf); // 2 x float32 (4 bytes each)
8
9console.log("Uint8 length:", u8.length);
10console.log("Int16 length:", i16.length);
11console.log("Float32 length:", f32.length);- 在同一個
ArrayBuffer上建立多個視圖,可以用不同型別或細度來讀寫相同記憶體。視圖的length是元素數量,byteLength則是位元組數。
寫入與讀取(位元組層級操作)
當你將值寫入 TypedArray 時,記憶體中的對應位元組會被更新。用不同的視圖讀取同一個緩衝區時,可以看到變化。
1// Demonstrate writing via one view and reading via another
2const buffer2 = new ArrayBuffer(4);
3const u8view = new Uint8Array(buffer2);
4const u32view = new Uint32Array(buffer2);
5
6u8view[0] = 0x78;
7u8view[1] = 0x56;
8u8view[2] = 0x34;
9u8view[3] = 0x12;
10
11console.log("Uint8 bytes:", Array.from(u8view)); // [120, 86, 52, 18]
12console.log("Uint32 value (platform endianness):", u32view[0]); // value depends on endianness
- 在此範例中,我們先寫入一段位元組序列,然後以 32 位元整數讀取相同區域。請注意,輸出結果會根據執行環境的位元組序(endianness)而有所不同。
位元組序(Endianness)與 DataView
如需控制與環境相關的位元組序問題,DataView 十分實用。DataView 允許你在讀寫時指定位元組序。
1// Use DataView to read/write with explicit endianness control
2const b = new ArrayBuffer(4);
3const dv = new DataView(b);
4
5// write little-endian 32-bit integer
6dv.setUint32(0, 0x12345678, true);
7
8// read as little-endian and big-endian
9const little = dv.getUint32(0, true);
10const big = dv.getUint32(0, false);
11
12console.log("little-endian read:", little.toString(16)); // "12345678"
13console.log("big-endian read:", big.toString(16)); // different value
DataView是一個方便用於細緻讀寫位元組的機制。它支援各種型別,例如有號或無號整數、浮點數,並可明確指定位元組序(Endianness),實作二進位協議時非常實用。
subarray 與 slice 的差異
TypedArray 的 subarray 會傳回共用原緩衝區的視圖,而 slice 則傳回一份新的複本。依你所使用的方法,效能與副作用會有所不同。
1const base = new Uint8Array([1, 2, 3, 4, 5]);
2
3const shared = base.subarray(1, 4); // shares underlying buffer
4const copied = base.slice(1, 4); // copies data
5
6shared[0] = 99;
7console.log("base after shared modification:", base); // shows change
8console.log("copied remains:", copied); // unaffected
- 如果你修改了共用的視圖,原始的陣列也會被改變,這可能會導致非預期的副作用。如果你想要安全地保留原始陣列,可以事先使用
slice()建立一份拷貝。
緩衝區複製與型別轉換(TypedArray 間的轉換)
我們將說明如何在不同 TypedArray 間複製資料及使用 set 方法貼上部分資料。
1// Copy and convert between typed arrays
2const src = new Float32Array([1.5, 2.5, 3.5]);
3const dst = new Uint16Array(src.length);
4
5// Convert by mapping (explicit conversion)
6for (let i = 0; i < src.length; i++) {
7 dst[i] = Math.round(src[i]); // simple conversion rule
8}
9console.log("converted dst:", Array.from(dst)); // [2, 2, 4]
10
11// Using set to copy bytes (requires compatible underlying buffer or same element type)
12const src2 = new Uint8Array([10, 20, 30]);
13const dst2 = new Uint8Array(6);
14dst2.set(src2, 2); // copy src2 into dst2 starting at index 2
15console.log("dst2 after set:", Array.from(dst2)); // [0,0,10,20,30,0]
- 若元素型別不同,需進行數值轉換(如四捨五入、檢查範圍),不能直接複製。
set可實現相同元素型別的TypedArray間快速複製。
實作範例:二進位協議剖析器(簡單實作)
這裡介紹一個簡單的剖析器範例,處理由 1 位元組型別、2 位元組資料長度與後續內容(payload)組成的固定格式二進位資料。
1// Simple binary message parser:
2// format: [type: uint8][length: uint16 BE][payload: length bytes]
3function parseMessages(buffer: ArrayBuffer) {
4 const dv = new DataView(buffer);
5 let offset = 0;
6 const messages: { type: number; payload: Uint8Array }[] = [];
7
8 while (offset + 3 <= dv.byteLength) {
9 const type = dv.getUint8(offset);
10 const length = dv.getUint16(offset + 1, false); // big-endian
11 offset += 3;
12 if (offset + length > dv.byteLength) break; // truncated
13 const payload = new Uint8Array(buffer, offset, length);
14 messages.push({ type, payload });
15 offset += length;
16 }
17
18 return messages;
19}
20
21// Example usage
22const buf = new ArrayBuffer(1 + 2 + 3 + 1 + 2 + 2); // two messages
23const dv = new DataView(buf);
24let off = 0;
25// first message: type=1, length=3, payload=[1,2,3]
26dv.setUint8(off, 1); dv.setUint16(off + 1, 3, false); off += 3;
27new Uint8Array(buf, off, 3).set([1, 2, 3]); off += 3;
28// second message: type=2, length=2, payload=[9,8]
29dv.setUint8(off, 2); dv.setUint16(off + 1, 2, false); off += 3;
30new Uint8Array(buf, off, 2).set([9, 8]);
31
32console.log(parseMessages(buf));- 在這個範例中,標頭是使用
DataView讀取,而有效負載的片段則是用Uint8Array建立的。位元組序與緩衝區長度的檢查都很重要。
Web API 與 TypedArray(範例:取得二進位資料)
這是使用 TypedArray 處理網路請求取得的 ArrayBuffer 的典型範例。
1// Example of fetching binary and mapping to typed array
2async function fetchBinary(url: string) {
3 const res = await fetch(url);
4 const ab = await res.arrayBuffer();
5 const view = new Uint8Array(ab);
6 // process view...
7 console.log("received bytes:", view.length);
8 return view;
9}
10
11// Usage (call in async context)
12// fetchBinary("/path/to/file.bin");
- 你可以直接將
Response.arrayBuffer()取得的ArrayBuffer傳給TypedArray。這常用於圖像、音訊或自訂二進位協議的處理。
效能建議與常見陷阱
以下是使用 TypedArray 時實用的「效能建議」與「常見陷阱」:。
-
避免不必要的複製 為高效處理大數據,可用
subarray建立部分視圖,或在多個視圖間共用同一ArrayBuffer,以減少不必要的複製。 -
注意位元組序(Endianness) 多數網路通訊或檔案格式都指定了資料的位元組順序(byte order)。使用
DataView可在讀寫時明確指定位元組序,避免誤解資料內容。 -
注意各型別的數值範圍 例如,
Uint8只能表示 0 到 255 的值。如果你輸入負值,可能會發生截斷或數值溢位,因此應該根據需要定義轉換規則。 -
考慮垃圾回收壓力 頻繁地重新建立大型的
ArrayBuffer會增加記憶體管理的負擔。在對效能要求嚴格的情況下,你可以考慮設計程式碼以盡可能重複使用現有的緩衝區。
總結
TypedArray 是一種快速高效處理二進位資料的機制。結合 ArrayBuffer(預留固定長度的位元組區域)與 TypedArray 或 DataView(以特定型別讀寫資料),可以靈活進行二進位操作。根據你的使用情境,可以選擇使用 subarray/slice 或 DataView,並在設計實作時注意位元組順序與複製的問題。
您可以在我們的 YouTube 頻道上使用 Visual Studio Code 來跟隨上述文章一起學習。 請也查看我們的 YouTube 頻道。