در ES 5 تنها آرایه (Array) و آبجکت (Object) را به عنوان ساختار دادهایی، به صورت توکار در اختیار داریم.
Array یک کالکشن مبتنی بر ایندکس است. همچنین میتوان هر نوع مقداری را در آن ذخیره کرد:
var collection = ['a', 1, /3/, {}];
یعنی هر کدام از اعضای آرایه میتوانند جنس متفاوتی داشته باشند. همانطور که در کد فوق مشاهده میکنید اعضای آرایه به ترتیب از کاراکتر، عدد، عبارت با قاعده و در نهایت یک شیء خالی تشکیل شده است. همانطور که عنوان شد آرایهها در جاوا اسکریپت همانند دیگر زبانهای برنامهنویسی مبتنی بر ایندکس هستند، یعنی میتوان براساس ایندکس به هر کدام از اعضای آرایه دسترسی داشت:
میتوان از پراپرتی length نیز برای دریافت سایز آرایه استفاده کرد:
همانطور که در مثال ابتدای بحث مشاهده کردید، آرایهها در جاوا اسکریپت توسط سینتکس [] قابل تعریف هستند. تعدادی تابع توکار برای کار با آرایهها موجود است که در
اینجا میتوانید لیست کامل آنها را مشاهد نمائید. همچنین میتوانید از کتابخانههای دیگری مانند Underscore.js که در واقع هدف آنها افزودن یکسری قابلیتها به جاوا اسکریپت است، استفاده کنید:
var numbers = [1, 2, 3];
_.each(numbers, function (num) {
write(num);
});
در ES 6 تعدادی تابع جدید به Array اضافه شده که کار با آرایهها را سادهتر کرده است. در ادامه تعدادی از این توابع را بررسی خواهیم کرد.
تابع find
این تابع از ورودی، یک callback را گرفته و نتایج یافته شده را در خروجی برمیگرداند:
var ary = [1, 5, 10];
var match = ary.find(item => item > 8);
تابع findIndex این تابع مشابه تابع find عمل میکند با این تفاوت که در خروجی ایندکس عنصر یافته شده را برمیگرداند:
var match = ary.findIndex(item => item > 8);
تابع fill از این تابع میتوان جهت مقداردهی اعضای آرایه با پارامتر موردنظر استفاده کرد:
ary.find('a');
// خروجی ["a", "a", "a"]
لازم به ذکر است، به این تابع میتوانیم دو پارامتر دیگر را نیز پاس دهیم:
var ary = [1, 5, 10, 5, 6];
ary.fill('a', 2, 3)
در کد فوق پارامتر دوم یعنی نقطه شروع و پارامتر سوم یعنی نقطه پایان:
// خروجی
[ 1, 5, "a", 5, 6 ]
تابع copyWithin با کمک این تابع میتوانیم قسمتی از یک آرایه را کپی کرده و در محل دیگری از آرایه ذخیره کنیم:
[1, 2, 3, 4, 5].copyWithin(0, 3);
کد فوق از نقطهی سوم شروع به کپی کردن آیتمها کرده و آنها را در موقعیت صفرم آرایه به بعد قرار میدهد. در نتیجه خروجی آن به این صورت است:
لازم به ذکر است که یک پارامتر سوم را نیز میتوانیم جهت تعیین نقطهی پایان به تابع فوق اضافه کنیم:
[1, 2, 3, 4, 5].copyWithin(0, 3, 4);
// خروجی
// [4, 2, 3, 4, 5]
در ES 6 علاوه بر سینتکس literal میتوان از سازندهی کلاس Array نیز جهت تعریف آرایهها، استفاده کرد:
var ary = new Array(1, 2);
کد فوق یک آرایه را با دو مقدار 1 و 2 ایجاد میکند. اگر بخواهیم یک آیتم جدید را به آرایهی فوق اضافه کنیم، باید آن را نیز به پارامترهای فوق اضافه کرد:
var ary = new Array(1, 2, 3);
ممکن است فکر کنید توسط کد زیر آرایهایی تنها با یک آیتم برای ما ایجاد خواهد شد:
در واقع کد فوق یک آرایه با اندازهی سه و محتوای undefined را برای شما ایجاد خواهد کرد. در نتیجه برای ایجاد آرایهایی با یک آیتم و مقدار 3 باید از متد Of کلاس Array استفاده کنیم:
Set
Set یک ساختار دادهایی جدید در ES 6 است. این ساختار دادهایی امکان تعریف کالکشنی از مقادیر را به صورت unique، در اختیارمان قرار میدهد. برخلاف آرایهها مقادیر درون Set نمیتواند یکسان باشند. در کد زیر نحوهی ایجاد یک Set نشان داده شده است:
var set = new Set();
set.add(1);
set.add(2);
set.add(3);
console.log(set.size); // logs 3
Set به صورت یک مجموعهی
Iterable است یعنی میتوان اعضای این مجموعه را آیتم به آیتم پیمایش کرد. همانطور که در کد فوق مشاهده میکنید توسط add میتوانیم آیتم جدیدی را به مجموعه اضافه کنیم. همچنین اگر مایل بودید میتوانید مجموعه را توسط یک آرایه به صورت زیر نیز مقداردهی کنید:
var set = new Set([1, 2, 3]);
console.log(set.size); // logs 3
از توابع has, delete, clear نیز به ترتیب میتوان جهت خالی کردن مجموعه، حذف یک آیتم از مجموعه و بررسی یک آیتم در مجموعه استفاده کرد:
var set = new Set();
set.has(1); // false
set.add(1);
set.has(1); // true
set.clear();
set.has(1); // false
set.add(1);
set.add(2);
set.size; // 2
set.delete(2);
set.size; // 1
از تابع feorach نیز میتوانیم برای حرکت بین آیتمهای مجموعه استفاده کنیم:
var set = new Set();
set.add('Vahid');
set.add('Sirwan');
var i = 0;
set.forEach(item => i++);
console.log(i);
همچنین از سینتکس for...of نیز میتوان برای پیمایش مجموعه استفاده کرد:
var set = new Set();
set.add('Vahid');
set.add('Sirwan');
var i = 0;
for(let item of set) {
i++;
}
console.log(i);
Set دارای یک تابع دیگر با نام entries است. با کمک این تابع یک iterator از مجموعه برگردانده خواهد شد که با کمک تابع next میتوان به عناصر بعدی مجموعه دسترسی پیدا کرد:
var set = new Set();
set.add("Sirwan");
set.add(1);
set.add("Afifi");
var setIter = set.entries();
console.log(setIter.next().value); // ["Sirwan", "Sirwan"]
console.log(setIter.next().value); // [1, 1]
console.log(setIter.next().value); // ["Afifi", "Afifi"]
Map برخلاف Set که یک مجموعه از مقادیر (values) است، Map یک مجموعه از کلید/مقدار (key/value) میباشد. در اینجا نیز کلیدها باید unique باشند. همچنین میتوان از هر نوعی برای کلید استفاده کرد. برای افزودن یک مقدار به این مجموعه باید از تابع set استفاه کنیم:
var map = new Map();
map.set('name', 'Sirwan');
map.get('name'); // Sirwan
همانطور که مشاهده میکنید توسط تابع get نیز میتوانیم با استفاده از کلید، به مقدار آن دسترسی داشته باشیم. همچنین میتوانیم آرایهایی از آرایهها را به عنوان کلید در یک Map ذخیره کنیم:
var map = new Map([['name', 'Sirwan'], ['age', 27]]);
map.has('age'); // true
map.get('age'); // 27
map.get('name'); // Sirwan
نکتهایی که در استفاده از Map باید به آن دقت کنید این است که در اینجا هیچ تبدیل نوعی را بر روی کلیدها نداریم:
var map = new Map();
map.set(1, true);
map.has("1"); // false
map.set("1", true);
map.has("1"); // true
همانند Set برای Map نیز میتوانیم از توابع delete و clear استفاده کنیم. برای استفاده از foreach باید برای callback دو پارامتر را ارائه دهیم. یکی برای value و دیگری برای key:
var map = new Map([['name', 'Sirwan'], ['age', 27]]);
var i = 0;
map.forEach(function (value, key) {
i++;
});
console.log(i); // log 2
برای سینتکس for...of نیز میتوانیم به اینصورت عمل کنیم:
for (var [key, value] of map) {
i++;
}
شاید بپرسید که همین کار را میتوان با استفاده از آرایهها نیز انجام داد و چه نیازی به یک ساختار دادهایی جدید است؟
اگر بخواهید Map را با استفاده از آرایهها شبیهسازی کنید باید از Associative Arrays استفاده کنید؛ به زبان ساده در اینحالت به جای استفاده از عدد به جای ایندکس میتوان رشتهها نیز استفاده کرد. به عنوان مثال کد زیر را در نظر بگیرید:
var newArray = new Array();
newArray["name"] = "Sirwan";
newArray["lastName"] = "Afifi";
در اینجا ایندکسها به ترتیب name و lastName هستند و به عنوان کلید مورد استفاده قرار میگیرند. کلیدها نیز به مقادیر "Sirwan" و "Afifi" مپ شدهاند. حالت فوق شبیه به یک دیکشنری عمل میکند. اما همانطور که عنوان شد در اینجا کلید به صورت رشتهایی است و نمیتوان از اشیاء به عنوان کلید استفاده کرد؛ زیرا در نهایت تبدیل به رشته خواهند شد:
let user1 = { name: "Vahid" };
let user2 = { name: "Sirwan" };
let result = {};
result[user1] = 10;
result[user2] = 20;
console.log(result[user1]); // logs 20
console.log(result[user2]); // logs 20
در کد فوق هر کدام از شیءها را به عنوان کلید در نظر گرفتهایم و برای هر کدام مقادیر 10 و 20 را ست کردهایم. اما خروجی هر کدام 20 است؛ در حالیکه باید به ترتیب عدد 10 و سپس عدد 20 در خروجی نمایش داده شود. دلیل آن نیز کاملاً مشخص است زیرا اگر در جاوا اسکریپت برای یک شیء تابع toString را فراخوانی کنیم، مقدار "[object object]" در خروجی نمایش داده خواهد شد. در نتیجه در کد فوق در واقع هر بار ایندکس "[object object]" را بهروز رسانی کردهایم:
result["[object object]"] = 10;
result["[object object]"] = 20;
console.log(result["[object object]"]); // logs 20
console.log(result["[object object]"]); // logs 20
WeakMap and WeakSet فرض کنید درون DOM سه عنصر div دارید و میخواهید این سه div را درون یک Set ذخیره کنید:
در اینحالت آیتمهای درون Set ارجاع مستقیمی را به عناصر موجود در DOM دارند. اکنون حالتی را در نظر بگیرید که بخواهیم یکی از عناصر موجود درون DOM را حذف کنیم. در اینحالت آیتم درون Set که به این عنصر اشاره دارد هنوز حذف نشده است و همچنان ارجاعی را به آن عنصر دارد. بنابراین تا زمانیکه آیتم از Set حذف نشود Garbage Collector نمیتواند حافظهی اختصاص داده شده را مجدداً بازیابی کند. در نتیجه استفاده از Set و یا Map در چنین سناریوهایی منجر به نشتی حافظه خواهد شد. برای حل این مشکل میتوانیم از WeakMap و یا WeakSet استفاده کنیم. در اینحالت WeakMap و WeakSet ارجاع مستقیمی به اشیایی که به آنها اضافه میشوند، ندارند. در نتیجه GC به راحتی میتواند حافظهی اختصاص داده شده را بعد از حذف اشیاء بازیابی کند.
صرفنظر از رفع مشکل حافظه، WeakMap و WeakSet شبیه به Map و Set عمل میکنند، اما یکسری محدودیتهایی در استفاده از آنها وجود دارد:
- WeakMap و WeakSet فاقد پراپرتیهای size, entries, values و متد foreach هستند.
- WeakMap همچنین فاقد keys است.