TypedArray trong TypeScript
Bài viết này giải thích về TypedArray trong TypeScript.
Chúng tôi sẽ giải thích về TypedArray trong TypeScript, bao gồm cả các ví dụ thực tế.
YouTube Video
TypedArray trong TypeScript
TypedArray là một cơ chế để xử lý dữ liệu nhị phân một cách hiệu quả. Nó đặc biệt hữu ích cho các thao tác nhị phân cấp thấp như dữ liệu hình ảnh lớn, luồng byte mạng và mảng số cho WebGL.
Cách tạo một ArrayBuffer
ArrayBuffer đại diện cho một vùng byte có độ dài cố định. Đầu tiên, tạo một buffer và kiểm tra kích thước cũng như độ dài tính bằng byte của nó.
1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
- Đoạn mã này tạo một vùng trống gồm 16 byte.
ArrayBuffertự nó không có chức năng đọc/ghi, do đó bạn truy cập nó thông quaTypedArrayhoặcDataView.
Các loại TypedArray
Có nhiều loại TypedArray, như các loại dưới đây. Chúng khác nhau tùy thuộc vào kiểu dữ liệu và kích thước mà chúng xử lý.
| TypedArray | Kiểu dữ liệu | Kích thước bit |
|---|---|---|
Int8Array |
Số nguyên 8-bit | 8 bit |
Uint8Array |
Số nguyên không dấu 8-bit | 8 bit |
Uint8ClampedArray |
Số nguyên không dấu 8-bit đã kẹp | 8 bit |
Int16Array |
Số nguyên 16-bit | 16 bit |
Uint16Array |
Số nguyên không dấu 16-bit | 16 bit |
Int32Array |
Số nguyên 32-bit | 32 bit |
Uint32Array |
Số nguyên không dấu 32-bit | 32 bit |
Float32Array |
Số dấu phẩy động 32 bit | 32 bit |
Float64Array |
Số dấu phẩy động 64 bit | 64 bit |
TypedArray cơ bản (Uint8Array, Int16Array, Float32Array, v.v.)
TypedArray tạo ra một 'khung nhìn có kiểu' trên ArrayBuffer. Dưới đây là các ví dụ về cách tạo và sử dụng một số TypedArray tiêu biểu.
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);- Bằng cách tạo nhiều khung nhìn trên cùng một
ArrayBuffer, bạn có thể đọc và ghi cùng một vùng nhớ với các kiểu hoặc độ chi tiết khác nhau.lengthcủa khung nhìn là số lượng phần tử, cònbyteLengthlà số lượng byte.
Ghi và đọc (các thao tác ở cấp byte)
Khi bạn ghi một giá trị vào TypedArray, các byte tương ứng trong bộ nhớ sẽ được cập nhật. Bạn có thể thấy sự thay đổi khi đọc cùng một buffer với một khung nhìn khác.
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
- Trong ví dụ này, chúng ta ghi một dãy byte sau đó đọc cùng khu vực đó dưới dạng số nguyên 32 bit. Lưu ý rằng đầu ra phụ thuộc vào kiểu sắp xếp byte (endianness) của môi trường thực thi.
Endianness (thứ tự byte) và DataView
DataView rất hữu ích nếu bạn muốn kiểm soát các vấn đề về endianness phụ thuộc vào môi trường. DataView cho phép bạn xác định endianness khi đọc và ghi.
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
DataViewlà một cơ chế thuận tiện để đọc và ghi byte một cách chi tiết. Nó hỗ trợ nhiều loại dữ liệu, chẳng hạn như số nguyên có dấu hoặc không dấu và số thực, đồng thời cho phép bạn xác định rõ ràng endianness (thứ tự byte), điều này rất hữu ích khi triển khai các giao thức nhị phân.
Sự khác biệt giữa subarray và slice
subarray của một TypedArray trả về một khung nhìn chia sẻ buffer gốc, còn slice trả về một bản sao mới. Hiệu năng và tác dụng phụ sẽ khác nhau tùy thuộc vào cái bạn sử dụng.
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
- Nếu bạn thay đổi chế độ xem được chia sẻ, mảng gốc cũng sẽ thay đổi, điều này có thể gây ra các tác dụng phụ ngoài ý muốn. Nếu bạn muốn bảo toàn mảng gốc một cách an toàn, bạn có thể tạo một bản sao trước bằng cách sử dụng
slice().
Sao chép buffer và chuyển đổi kiểu (chuyển đổi giữa các TypedArray)
Chúng tôi sẽ giải thích cách sao chép dữ liệu giữa các TypedArray và cách dán một phần dữ liệu bằng phương thức 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]
- Nếu kiểu phần tử khác nhau, bạn cần chuyển đổi giá trị, ví dụ như làm tròn hoặc kiểm tra phạm vi, thay vì chỉ đơn giản sao chép.
setcho phép sao chép nhanh giữa cácTypedArraycùng kiểu phần tử.
Ví dụ thực tế: Trình phân tích cú pháp giao thức nhị phân (triển khai đơn giản)
Tại đây, chúng tôi giới thiệu một ví dụ trình phân tích cú pháp đơn giản, đọc dữ liệu nhị phân định dạng cố định gồm một byte loại, hai byte chiều dài dữ liệu và một payload theo sau.
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));- Trong ví dụ này, phần tiêu đề được đọc bằng
DataView, và phần cắt của payload được tạo bằngUint8Array. Kiểm tra endianness và độ dài buffer rất quan trọng.
Web API và TypedArray (Ví dụ: lấy dữ liệu nhị phân)
Đây là một ví dụ điển hình về việc xử lý ArrayBuffer thu được từ một yêu cầu mạng bằng cách sử dụng TypedArray.
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");
- Bạn có thể truyền trực tiếp
ArrayBufferthu được bằngResponse.arrayBuffer()vào mộtTypedArray. Nó được sử dụng cho hình ảnh, âm thanh hoặc các giao thức nhị phân tùy chỉnh.
Mẹo về hiệu năng và những cạm bẫy thường gặp
Dưới đây là một số 'mẹo hiệu năng' và 'cạm bẫy thường gặp' hữu ích khi sử dụng các TypedArray:.
-
Tránh sao chép không cần thiết Để xử lý dữ liệu lớn một cách hiệu quả, bạn có thể giảm việc sao chép không cần thiết bằng cách tạo các khung nhìn phân đoạn với
subarrayhoặc chia sẻ cùng mộtArrayBuffergiữa nhiều khung nhìn. -
Hãy chú ý đến endianness Đối với giao tiếp mạng hoặc định dạng tệp, thứ tự dữ liệu (thứ tự byte) thường được xác định rõ. Việc sử dụng
DataViewcho phép bạn xác định rõ endianness khi đọc và ghi, giúp tránh việc diễn giải sai ngoài ý muốn. -
Chú ý đến dải giá trị của từng kiểu dữ liệu Ví dụ,
Uint8chỉ có thể biểu diễn các giá trị từ 0 đến 255. Nếu bạn nhập một giá trị âm, có thể xảy ra hiện tượng cắt bớt hoặc tràn giá trị, vì vậy bạn nên xác định các quy tắc chuyển đổi phù hợp nếu cần. -
Cân nhắc tác động lên hệ thống thu gom rác (garbage collection) Việc tạo lại thường xuyên các
ArrayBufferlớn sẽ làm tăng gánh nặng cho việc quản lý bộ nhớ. Trong các tình huống đòi hỏi hiệu suất cao, bạn có thể cân nhắc thiết kế mã để tái sử dụng bộ đệm hiện có càng nhiều càng tốt.
Tóm tắt
TypedArray là một cơ chế để xử lý dữ liệu nhị phân một cách nhanh chóng và hiệu quả. Bằng cách kết hợp ArrayBuffer, nơi dành một vùng byte có độ dài cố định, với TypedArray hoặc DataView, có thể đọc và ghi nội dung bằng các kiểu dữ liệu cụ thể, bạn có thể thực hiện các thao tác nhị phân linh hoạt. Tùy thuộc vào trường hợp sử dụng của bạn, bạn có thể chọn giữa việc sử dụng subarray/slice hoặc DataView, đồng thời thiết kế phương án triển khai chú ý đến thứ tự byte và việc sao chép.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.