برای شروع کار، اول از همه باید یک بافر بسازیم. در این مثال، ما یک بافر با طول ثابت ۱۶ بایت ایجاد میکنیم:
const buffer = new ArrayBuffer(16);
در این مرحله، ما یک تکه از حافظه را در اختیار داریم که تمام بایتهای آن به صورت پیشفرض با مقدار 0 مقداردهی اولیه شدهاند. البته هنوز کار خاصی نمیتوانیم با آن انجام دهیم. برای نمونه، فقط میتوانیم بررسی کنیم که آیا سایز بافر درست است یا خیر:
if (buffer.byteLength === 16) {
console.log("بله، درست است؛ ۱۶ بایت است.");
} else {
console.log("ای وای، سایز بافر اشتباه است!");
}
قبل از اینکه بتوانیم واقعاً با این بافر کار کنیم، باید یک «نما» (View) بسازیم. بیا نمایی بسازیم که با دادههای درون بافر، مثل یک آرایه از اعداد صحیح ۳2 بیتیِ علامهدار (32-bit signed integers) رفتار کند:
const int32View = new Int32Array(buffer);
حالا میتوانیم دقیقاً مثل یک آرایه معمولی به خانههای آن دسترسی داشته باشیم و مقداردهیشان کنیم:
for (let i = 0; i < int32View.length; i++) {
int32View[i] = i * 2;
}
این حلقه، ۴ خانه یا ورودیِ آرایه را پر میکند (۴ ورودی که هر کدام ۴ بایت حجم دارند، در مجموع همان ۱۶ بایت حافظه ما را تشکیل میدهند). مقادیر ذخیرهشده به ترتیب 0، 2، 4 و 6 خواهند بود.
داستان زمانی خیلی جذاب میشود که بدانید میتوانید چندین نمای مختلف و متفاوت را روی یک داده و بافر مشترک ایجاد کنید! به عنوان مثال، در ادامه کدهای بالا، میتوانیم اینطوری بنویسیم:
const int16View = new Int16Array(buffer);
for (let i = 0; i < int16View.length; i++) {
console.log(`Entry ${i}: ${int16View[i]}`);
}
در اینجا ما یک نمای اعداد صحیح ۱۶ بیتی (Int16Array) ساختیم که دقیقاً دارد از همان بافر مشترکِ نمای ۳۲ بیتی قبلی استفاده میکند. حالا وقتی تمام مقادیر داخل بافر را به عنوان اعداد ۱۶ بیتی در خروجی چاپ میکنیم، به این رشته از اعداد میرسیم: 0, 0, 2, 0, 4, 0, 6, 0 (البته با فرض اینکه سیستم شما از رمزگذاری Little-endian استفاده کند).
بیا ببینیم چطور یک حافظه مشترک، با دو نمای مختلف دیده میشود:
نما با فواصل ۱۶ بیتی | 0 | 0 | 2 | 0 | 4 | 0 | 6 | 0 |
-------------------|-------------------------------------------------------
نما با فواصل ۳۲ بیتی | 0 | 2 | 4 | 6 |
-------------------|-------------------------------------------------------
بافر اصلی (بایتها) | 00 00 00 00 | 02 00 00 00 | 04 00 00 00 | 06 00 00 00 |
بیا یک قدم فراتر برویم؛ به این سناریو نگاه کنید:
int16View[0] = 32;
console.log(`اندازه خانه صفر در آرایه ۳۲ بیتی الان شده است: ${int32View[0]}`);
خروجی این کد عبارت است از: "اندازه خانه صفر در آرایه ۳۲ بیتی الان شده است: 32". این یعنی هر دو آرایه واقعاً دارند به یک دادهی واحد در بافر نگاه میکنند، فقط فرمت و عینکِ تفسیرِ آنها با هم فرق دارد!
نما با فواصل ۱۶ بیتی | 32 | 0 | 2 | 0 | 4 | 0 | 6 | 0 |
-------------------|-------------------------------------------------------
نما با فواصل ۳۲ بیتی | 32 | 2 | 4 | 6 |
-------------------|-------------------------------------------------------
بافر اصلی (بایتها) | 20 00 00 00 | 02 00 00 00 | 04 00 00 00 | 06 00 00 00 |
شما میتوانید این کار را با هر نوع نمای دیگری هم انجام دهید. البته حواستان باشد اگر دادهای را به صورت عدد صحیح ذخیره کنید و بعد بخواهید آن را به عنوان یک عدد اعشاری (Floating-point) بخوانید، احتمالاً به یک نتیجه عجیب و غریب میرسید؛ چون موتور جاوااسکریپت بیتها را کاملاً متفاوت تفسیر میکند:
const float32View = new Float32Array(buffer);
console.log(float32View[0]); // خروجی یک عدد عجیب میشود: 4.484155085839415e-44
بافرها همیشه نمایندهی اعداد نیستند. به عنوان مثال، خواندن یک فایل از سیستم میتواند به شما یک بافر حاوی دادههای متنی بدهد. شما میتوانید این دادههای متنی را به کمک یک آرایه نوعدار از بافر بیرون بکشید.
در مثال زیر، متن UTF-8 را با استفاده از API وبِ TextDecoder میخوانیم:
const buffer = new ArrayBuffer(8);
const uint8 = new Uint8Array(buffer);
// دادهها را اینجا دستی مینویسیم، اما تصور کنید این دادهها از قبل داخل بافر بودهاند:
uint8.set([228, 189, 160, 229, 165, 189]);
const text = new TextDecoder().decode(uint8);
console.log(text); // خروجی: "你好" (به معنی سلام)
در مثال زیر هم متن UTF-16 را با استفاده از متد سنتی String.fromCharCode() بازیابی میکنیم:
const buffer = new ArrayBuffer(8);
const uint16 = new Uint16Array(buffer);
// دادهها به صورت کدهای هگزادسیمال دستی نوشته میشوند:
uint16.set([0x4f60, 0x597d]);
const text = String.fromCharCode(...uint16);
console.log(text); // خروجی: "你好"
شما با ترکیب کردن یک بافر واحد با چندین نمای مختلف (با نوعهای دادهای متفاوت و شروع از آفستهای مختلف در بافر)، میتوانید با اشیاء دادهای کار کنید که شامل چندین نوع دادهی مختلف هستند. این تکنیک به شما اجازه میدهد که به راحتی با ساختارهای دادهای پیچیده در پروژههای WebGL یا فایلهای دیتای سیستمی تعامل داشته باشید.
تصور کنید یک ساختار جاافتاده در زبان C (یک C struct) به شکل زیر داریم:
struct someStruct {
unsigned long id;
char username[16];
float amountDue;
};
برای دسترسی به بافری که حاوی اطلاعاتی با این فرمت و چیدمان است، میتوانیم در جاوااسکریپت اینطوری عمل کنیم:
const buffer = new ArrayBuffer(24);
// ... در این قسمت فرض کنید دادهها را درون بافر خواندهایم ...
// شناسه آیدی (شروع از بایت ۰، به طول ۱ عنصر ۳۲ بیتی)
const idView = new Uint32Array(buffer, 0, 1);
// نام کاربری (شروع از بایت ۴، به طول ۱۶ عنصر ۱ بایت)
const usernameView = new Uint8Array(buffer, 4, 16);
// مبلغ بدهی (شروع از بایت ۲۰، به طول ۱ عنصر اعشاری)
const amountDueView = new Float32Array(buffer, 20, 1);
حالا خیلی راحت میتوانید مثلاً به مبلغ بدهی از طریق اندیس صفرِ نمای مربوطه دسترسی داشته باشید: amountDueView[0].
⚠️ یک نکته فنی: تراز کردن ساختار دادهها (Data structure alignment) در ساختارهای زبان C کاملاً به پلتفرم و سیستمعامل بستگی دارد. بنابراین هنگام کار با این دادهها، باید تفاوتهای احتمالی در پدینگها (Padding یا همان فاصلههای خالی بین بایتها) را کاملاً در نظر بگیرید.
گاهی اوقات بعد از اینکه پردازش دادهها را در آرایه نوعدار تمام کردید، لازم است که آن را دوباره به یک آرایه معمولی جاوااسکریپت تبدیل کنید تا بتوانید از تمام متدهای پروتوتایپ آرایهها (Array.prototype) استفاده کنید.
برای این کار میتوانید از متد Array.from() استفاده کنید:
const typedArray = new Uint8Array([1, 2, 3, 4]);
const normalArray = Array.from(typedArray);
یا اینکه خیلی سادهتر، از سینتکس پخشکننده یا اسپرد (...) کمک بگیرید:
const typedArray = new Uint8Array([1, 2, 3, 4]);
const normalArray = [...typedArray];
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://developer.mozilla.org/en-US/docs/Web/JavaScript