
نوشته رو در وبلاگ یاvar بخونید: yavarjs.ir/posts/looping-over-arrays
در این نوشته چهار روش مختلف برای استفاده از حلقهها روی آرایهها رو مقایسه میکنیم.
for (let index=0; index < someArray.length; index++) { const elem = someArray[index]; // ··· }
for (const key in someArray) { console.log(key); }
someArray.forEach((elem, index) => { console.log(elem, index); });
for (const elem of someArray) { console.log(elem); }
حلقهی for-of در اغلب موارد بهترین انتخابه. در ادامه میبینم چرا.
حلقهی ساده for در جاوااسکریپت قدمت داره و از نسخهی ۱ ECMAScript وجود داشته. حلقه زیر اندیس و مقدار هر عنصر آرایه arr رو چاپ میکنه:
const arr = ['a', 'b', 'c']; arr.prop = 'property value'; for (let index=0; index < arr.length; index++) { const elem = arr[index]; console.log(index, elem); } // Output: // 0, 'a' // 1, 'b' // 2, 'c'
نقاط مثبت و منفی این حلقه چیه؟
حلقهی for-in به اندازهی حلقهی for قدمت داره. حلقهی زیر کلیدهای آرایهی arr رو چاپ میکنه:
const arr = ['a', 'b', 'c']; arr.prop = 'property value'; for (const key in arr) { console.log(key); } // Output: // '0' // '1' // '2' // 'prop'
for-in انتخاب خوبی برای حلقهزدن روی آرایههای نیست، چون:
این ویژگی for-in که تمام propertyهای به ارثرسیده رو مرور میکنه یه جا کاربرد داره: زمانی که میخوایم روی propertyهای قابل شمارش یه object حلقه بزنیم. اما حتی در اون مورد هم حلقه زدن روی زنجیره prototype به شکل دستی بهتره، چون کنترل بیشتری در اختیارمون میذاره.
با توجه به این که نه حلقهی for و نه حلقهی for-in انتخابهای خوبی برای حلقهزدن روی آرایهها نیستن، یه متد کمکی در ECMAScript 5 معرفی شد: ()Array.prototype.forEach:
const arr = ['a', 'b', 'c']; arr.prop = 'property value'; arr.forEach((elem, index) => { console.log(elem, index); }); // Output: // 'a', 0 // 'b', 1 // 'c', 2
این متد خیلی راحته: بدون نیاز به انجام کار زیاد ، هم دسترسی به اندیس و هم به عناصر آرایه رو میده. تابعهای فِلشی (Arrow functions) که در ES6 معرفی شدن، این متد رو از قبل هم زیباترش کرده.
معایب اصلی ()forEach. از این قراره:
یه راه حل برای خارج شدن از این حلقه هست: استفاده از ()some. که روی تمام عناصر آرایه حلقه میزنه و زمانی که تابع callback یه مقدار truthy (یه مقداری که بشه به معنی true تفسیرش کرد) رو برگردونه، متوقف میشه.
const arr = ['red', 'green', 'blue']; arr.some((elem, index) => { if (index >= 2) { return true; // از حلقه خارج میشه } console.log(elem); // رو برمیگردونه undefined تابع به طور ضمنی مقدار // هست حلقه ادامه پیدا میکنه falsy که چون یه مقدار }); // Output: // 'red' // 'green'
این حلقه در ECMAScript 6 اضافه شد:
const arr = ['a', 'b', 'c']; arr.prop = 'property value'; for (const elem of arr) { console.log(elem); } // Output: // 'a' // 'b' // 'c'
حلقهی for-of برای حلقه زدن روی آرایهها خیلی خوبه، چون:
یه مزیت اضافهی for-of اینه که با اون نه فقط روی آرایهها بلکه روی آبجکتهای iterable هم میشه حلقه زد - برای مثال، روی Mapها:
const myMap = new Map() .set(false, 'no') .set(true, 'yes') ; for (const [key, value] of myMap) { console.log(key, value); } // Output: // false, 'no' // true, 'yes'
حلقه زدن روی myMap جفت [key, value] رو برمیگردونه که در کد بالا از روش destructre کردن برای دسترسی مستقیم به مقادیر این جفت استفاده کردیم.
متد ()keys. در آرایهها، یه iterable روی اندیسهای آرایه رو برمیگردونه:
const arr = ['chocolate', 'vanilla', 'strawberry']; for (const index of arr.keys()) { console.log(index); } // Output: // 0 // 1 // 2
متد ()entries. در آرایهها یه iterable روی جفتهای [index, value] برمیگردونه. با استفاده از for-of و destructuring خیلی راحت میتونیم روی مقادیر و اندیسهای آرایه به طور همزمان حلقه بزنیم:
const arr = ['chocolate', 'vanilla', 'strawberry']; for (const [index, value] of arr.entries()) { console.log(index, value); } // Output: // 0, 'chocolate' // 1, 'vanilla' // 2, 'strawberry'
همونطور که دیدیم، وقتی معیار کاربرد باشه حلقهی for-of از بقیهی روشها بهتره.
هر تفاوتی در سرعت (performance) بین این چهار روش حلقهزدن، در حالت عادی نباید اهمیتی داشته باشه. اگه اهمیت داره احتمالا داری یه کار خیلی سنگین از نظر محاسباتی انجام میدی و بنابراین استفاده از WebAssembly احتمالا گزینهی معقولتری باشه.
منبع: Looping over Arrays: for vs. for-in vs. .forEach() vs. for-of از وبلاگ 2ality - JavaScript and more