TypedArray ใน TypeScript
บทความนี้อธิบายเกี่ยวกับ TypedArray ใน TypeScript
เราจะอธิบายเกี่ยวกับ TypedArray ใน TypeScript พร้อมตัวอย่างการใช้งานจริง
YouTube Video
TypedArray ใน TypeScript
TypedArray เป็นกลไก สำหรับจัดการกับข้อมูลไบนารี่ได้อย่างมีประสิทธิภาพ มันมีประโยชน์อย่างยิ่งสำหรับการดำเนินการระดับต่ำกับข้อมูลไบนารี่ เช่น ข้อมูลภาพขนาดใหญ่ สตรีมไบต์ในเครือข่าย และอาร์เรย์ตัวเลขสำหรับ WebGL
วิธีสร้าง ArrayBuffer
ArrayBuffer คือพื้นที่ของไบต์ที่มีความยาวคงที่ ก่อนอื่น สร้าง buffer และตรวจสอบขนาดและความยาวเป็นไบต์ของมัน
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 มีหลายประเภท เช่น ต่อไปนี้ TypedArrays แต่ละแบบแตกต่างกันตามประเภทข้อมูลและขนาดที่จัดการได้
| 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 ไบต์ที่เกี่ยวข้องในหน่วยความจำจะถูกอัปเดตด้วย คุณสามารถเห็นการเปลี่ยนแปลงเมื่ออ่าน buffer เดียวกันด้วยวิวแบบอื่น
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 มีประโยชน์มากถ้าคุณต้องการควบคุมปัญหาเกี่ยวกับ endianness ที่ขึ้นกับสภาพแวดล้อม DataView ช่วยให้คุณกำหนด endianness ได้ขณะอ่านและเขียนข้อมูล
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เป็นกลไกที่สะดวกสำหรับการอ่านและเขียนไบต์อย่างละเอียด มันรองรับข้อมูลหลายรูปแบบ เช่น จำนวนเต็มแบบ signed หรือ unsigned และค่าทศนิยม รวมถึงสามารถกำหนด endianness ได้ชัดเจน ซึ่งมีประโยชน์มากเมื่อใช้งานกับไบนารี่โปรโตคอล
ความแตกต่างระหว่าง subarray และ slice
subarray ของ TypedArray จะคืนค่าเป็นวิวที่แชร์ buffer เดิม ขณะที่ slice จะคืนค่าเป็น buffer ใหม่ที่ถูกคัดลอกมา ประสิทธิภาพและผลข้างเคียงจะแตกต่างกันตามที่คุณเลือกใช้งาน
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()
การคัดลอก buffer และการแปลงชนิดข้อมูล (ระหว่าง 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ที่มีชนิดสมาชิกเดียวกันได้อย่างรวดเร็ว
ตัวอย่างในทางปฏิบัติ: ตัวแยกไบนารี่โปรโตคอล (การนำไปใช้แบบง่าย)
ที่นี่ เราขอนำเสนอตัวอย่าง parser ง่าย ๆ ที่อ่านข้อมูลไบนารีรูปแบบคงที่ ซึ่งประกอบด้วย ชนิดข้อมูล 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และกำหนดส่วนข้อมูล payload ด้วยUint8Arrayการเช็ค endianness และความยาว buffer มีความสำคัญ
Web API กับ TypedArray (ตัวอย่าง: การรับข้อมูลไบนารี)
นี่คือตัวอย่างมาตรฐานของการจัดการ ArrayBuffer ที่ได้จาก request เครือข่ายโดยใช้ 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");
- คุณสามารถส่ง
ArrayBufferที่ได้จากResponse.arrayBuffer()ไปยังTypedArrayได้โดยตรง โดยปกติจะใช้งานกับรูปภาพ เสียง หรือโปรโตคอลไบนารีแบบกำหนดเอง
เคล็ดลับด้านประสิทธิภาพและข้อควรระวังทั่วไป
นี่คือ 'เคล็ดลับด้านประสิทธิภาพ' และ 'ข้อควรระวัง' ที่ควรรู้เมื่อต้องใช้ TypedArray:
-
หลีกเลี่ยงการคัดลอกข้อมูลที่ไม่จำเป็น เพื่อประมวลผลข้อมูลขนาดใหญ่ได้อย่างมีประสิทธิภาพ ควรลดการคัดลอกโดยไม่จำเป็น ด้วยการสร้างวิวส่วนย่อยด้วย
subarrayหรือแชร์ArrayBufferร่วมกันหลาย ๆ วิว -
ระวังเรื่อง endianness ด้วย สำหรับการสื่อสารผ่านเครือข่ายหรือรูปแบบไฟล์ มักจะกำหนดลำดับการจัดเก็บข้อมูล (byte order) การใช้
DataViewช่วยให้คุณกำหนด endianness ได้ชัดเจนขณะอ่านและเขียน ป้องกันความเข้าใจผิดที่อาจเกิดจากการจัดลำดับไบต์ที่ต่างกัน -
โปรดระวังช่วงค่าที่แต่ละชนิดข้อมูลสามารถเก็บได้ ตัวอย่างเช่น
Uint8เก็บค่าได้เฉพาะ 0 ถึง 255 เท่านั้น หากคุณใส่ค่าติดลบ อาจเกิดการตัดทอนหรือการหมุนเวียนค่าขึ้นได้ คุณควรกำหนดกฎการแปลงค่าตามความเหมาะสม -
ควรพิจารณาภาระของการเก็บขยะ (garbage collection) การสร้าง
ArrayBufferที่มีขนาดใหญ่ใหม่บ่อยครั้ง จะเพิ่มภาระการจัดการหน่วยความจำ ในสถานการณ์ที่เน้นประสิทธิภาพ คุณควรพิจารณาออกแบบโค้ดเพื่อใช้ประโยชน์จากบัฟเฟอร์ที่มีอยู่อย่างเต็มที่
สรุป
TypedArray เป็นกลไกสำหรับจัดการข้อมูลไบนารี่ได้อย่างรวดเร็วและมีประสิทธิภาพ ด้วยการผสมผสาน ArrayBuffer ที่สงวนพื้นที่ไบต์แบบความยาวคงที่ กับ TypedArray หรือ DataView ที่อ่านและเขียนข้อมูลโดยใช้ชนิดข้อมูลที่เฉพาะเจาะจง คุณสามารถดำเนินการกับข้อมูลไบนารีได้อย่างยืดหยุ่น ขึ้นอยู่กับกรณีการใช้งานของคุณ คุณสามารถเลือกใช้ระหว่าง subarray/slice หรือ DataView และออกแบบการใช้งานโดยคำนึงถึง endianness และการทำสำเนา
คุณสามารถติดตามบทความข้างต้นโดยใช้ Visual Studio Code บนช่อง YouTube ของเรา กรุณาตรวจสอบช่อง YouTube ด้วย