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، مثل التالية۔ تختلف هذه الأنواع حسب نوع وحجم البيانات التي تعالجها.۔
| 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 مفيدة إذا كنت ترغب في التحكم في مسائل ترتيب البايتات حسب البيئة۔ يتيح لك 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
subarray من TypedArray يعيد عرضًا يشارك المخزن المؤقت الأصلي، بينما 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()۔
نسخ المخازن المؤقتة (buffers) وتحويل الأنواع (التحويل بين أنواع 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من نفس نوع العنصر۔
مثال عملي: محلل بروتوكول ثنائي (تنفيذ بسيط)
هنا، نقدم مثالاً بسيطاً لمحلل يقرأ بيانات ثنائية ذات تنسيق ثابت مكونة من نوع بحجم بايت واحد، وطول بيانات بحجم بايتين، وحمولة (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 (مثال: جلب بيانات ثنائية)
هذا مثال نموذجي على التعامل مع ArrayBuffer تم الحصول عليه من طلب شبكة باستخدام 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) بالنسبة للاتصال بالشبكة أو تنسيقات الملفات، غالباً ما يتم تحديد ترتيب البيانات (ترتيب البايتات)۔ استخدام
DataViewيتيح لك تحديد ترتيب البايتات صراحةً عند القراءة والكتابة، مما يمنع سوء التفسير غير المقصود۔ -
كن على دراية بنطاق القيم لكل نوع على سبيل المثال،
Uint8يمكنه فقط تمثيل القيم من 0 إلى 255۔ إذا أدخلت قيمة سالبة، فقد يحدث اقتطاع أو التفاف للقيمة، لذلك يجب عليك تحديد قواعد التحويل حسب الحاجة۔ -
ضع في اعتبارك ضغط جمع القمامة (garbage collection) إعادة إنشاء كائنات
ArrayBufferكبيرة باستمرار يزيد عبء إدارة الذاكرة۔ في الحالات التي تكون حساسة للأداء، قد تفكر في تصميم الكود لإعادة استخدام المخازن الموجودة قدر الإمكان۔
الملخص
TypedArray هي آلية للتعامل بسرعة وكفاءة مع البيانات الثنائية۔ من خلال الجمع بين ArrayBuffer، الذي يحجز منطقة بايت بطول ثابت، مع TypedArray أو DataView اللذَين يقرآن ويكتبان المحتوى بواسطة أنواع محددة، يمكنك إجراء عمليات ثنائية مرنة۔ اعتمادًا على حالة الاستخدام الخاصة بك، يمكنك الاختيار بين استخدام subarray/slice أو DataView، وتصميم التنفيذ مع الانتباه لترتيب البايتات والنسخ۔
يمكنك متابعة المقالة أعلاه باستخدام Visual Studio Code على قناتنا على YouTube.۔ يرجى التحقق من القناة على YouTube أيضًا.۔