گاهی اوقات زبان جاوااسکریپت میتواند پیچیده و گیجکننده باشد. دلیل آن نیز برخی از دستوراتی است که در ظاهر بسیار به هم شبیهاند اما در واقع تفاوتهای جزئی و اساسی با هم دارند. به عنوان مثال، دو متد map() و forEach() را در نظر بگیرید. این دو متد از رایجترین متدهای جاوااسکریپت هستند. اما آیا میدانید این دو چه تفاوتهایی باهم دارند؟ به نظر شما، چه زمانهایی باید از forEach() و در چه مواقعی باید از map() استفاده کرد؟
اگر این موضوع برای شما هم مبهم و گیجکننده است، با ما همراه باشید تا به طور کامل و جامع به این سوال اساسی پاسخ دهیم.
هر موقع که بخواهیم از طریق آرایه یک آیتم، عنصر یا عملیات را تکرار کنیم، اولین متدی که به ذهنمان خطور میکند، forEach() است. این متد در جاوااسکریپت یک جایگزین کاملا مناسب برای ایجاد حلقه است.
forEach() از طریق عناصر موجود در آرایه تکرار میشود و یک تابع را به تعداد همین عناصر در آرایه فراخوانی میکند. این تابع با سه آرگومان ورودی کار میکند و عملیات داخل forEach() را اجرا میکند.
برای اینکه کاملا متوجه چگونگی عملکرد forEach() شوید، به مثال زیر توجه کنید:
let temp = [1, 4, 9]; temp.forEach((item, index) => { return temp[index] = item * 3; }); // temp is now [3, 12, 27]
همانطور که میبینید، در ابتدا یک متغیر به نام temp تعریف شده که یک آرایه یک بعدی را در خود ذخیره کرده است. سپس با استفاده از حلقه forEach() تمامی عناصر آرایه به عدد ۳ ضرب شدهاند. در نهایت نتیجهای که حاصل میشود این است که مقادیر متغیر temp تغییر کرده و عناصر جدیدی را در خود ذخیره میکند.
متد map() نیز نقش ایجاد حلقه را در جاوااسکریپت دارد. نحوه عملکرد این متد به این ترتیب است که یک متغیر جدید ایجاد کرده و یک تابع را به تعداد عناصر آرایه فراخوانی میکند. سپس، عملیات نوشته شده در داخل حلقه اجرا میشود و به دنبال آن، نتایج به دست آمده در متغیر جدید ذخیره میشود.
به مثال زیر توجه کنید تا عملکرد map() را به طور کامل درک کنید:
let numbers = [1, 4, 9]; let triples = numbers.map((item) => { return item * 3; }); // numbers is still [1, 4, 9] // triples is [3, 12, 27]
در این مثال، متغیری به نام number تعریف شده است که دارای یک آرایه یک بعدی است. پس از آن، متد map() اجرا میشود و تمامی عناصر آرایه به عدد ۳ ضرب میشوند. سپس، نتایج به دست آمده در متغیر جدیدی به نام triples ذخیره میشود.
با توجه به تعاریف و توضیحاتی که در بالا گفته شد، آیا میتوانید حدس بزنید که تفاوت میان این دو متد در چیست؟
این دو متد بسیار به هم شبیهاند و نتایج مشابهی را ارائه میدهند. اما با کمی دقت متوجه میشوید که یک تفاوت اساسی با یکدیگر دارند.
متد forEach() پس از انجام عملیات لازم، محتویات آرایه اصلی را تغییر میدهد. بنابراین، پس از پایان عملیات، دیگر به آرایه قبلی دسترسی نداریم.
اما در متد map() اینگونه نیست! در این متد یک آرایه جدید تعریف شده و تغییرات مورد نیاز در این آرایه ذخیره میشود. در این صورت، آرایه اصلی دستنخورده باقی میماند و بدون تغییر همواره در دسترس برنامهنویس است.
حالا که متوجه تفاوتهای میان forEach() و map() شدید، هنگام ایجاد حلقه ترجیح میدهید که از کدام یک استفاده کنید؟ مسلم است که این موضوع به کاری که میخواهید انجام دهید، بستگی دارد.
اگر به دنبال ایجاد تغییر در آرایه هستید، متد map() گزینه مناسبتری است. چرا که تضمین میکند که آرایه اصلی تغییر نمیکند و هر زمان که تمایل داشتید میتوانید به آن دسترسی پیدا کنید. اما اگر تغییر یا عدم تغییر آرایه برایتان اهمیتی ندارد، میتوانید از متد forEach() استفاده کنید.
فقط باید به یک نکته بسیار مهم توجه کنید. متد forEach() تنها یک تابع را به تعداد عناصر موجود در آرایه فراخوانی میکند. به عبارت دیگر، بعد از استفاده از آن هیچ چیزی را return نمیکند. به همین خاطر، در ادامه برنامهنویسی دیگر نمیتوانید دوباره از آن آرایه استفاده کنید. در حالی که متد map() یک آرایه جدید از تابع را return میکند.
با تمام این اوصاف، به نظر میرسد که از لحاظ سرعت تفاوت بسیار کمی میان forEach() و map() وجود دارد. هرچند که میتوان گفت متد map() کمی سریعتر است، اما این تفاوت بسیار ناچیز و کم است. بنابراین، میتوانید با خیال راحت از هر کدام که تمایل داشتید استفاده کنید و نگرانی بابت سرعت عملکرد آنها نداشته باشید.
همانطور که گفتیم، متد map() بدون آنکه آرایه اصلی را تغییر دهد، مقادیر جدید را در آرایهای جدید ذخیره میکند. اما این تنها مزیت متد map() نیست.
یکی دیگر از مزیتهای map() این است که قابلیت ترکیب با سایر متدها را دارد. در حقیقت، زمانی که از map() استفاده میکنید، به راحتی میتوانید آن را با سایر متدها مانند filter() یا reduce() ترکیب کنید. این در حالی است که forEach() از این قابلیت محروم است.
اجازه دهید با یک مثال، این موضوع را بهتر توضیح دهیم:
let fruits = ['apple', 'banana', 'strawberry', 'orange']; fruits.forEach((fruit) => { console.log(fruit); }); // apple // banana // strawberry // orange // undefined
در مثال بالا، سه متد map()، filter() و reduce() با هم ترکیب شدهاند. متد map() یک تابع با نام toTripples را فراخوانی میکند. کار این تابع این است که تکتک مقادیر آرایه را ۳ برابر کرده و در آرایهای جدید ذخیره میکند.
پس از اینکه متد map() آرایه جدید را return میکند، حال نوبت filter() است که وارد عمل شود. این متد با فراخوانی تابع isOdd() تنها ارقام فرد داخل آرایه را تحویل متدreduce() میدهد. در نهایت این متد نیز مجموع اعداد فرد را محاسبه کرده و در خروجی چاپ میکند.
به عنوان مثال اگر آرایه ورودی [۱, ۲, ۳] باشد، پس از اجرای map() مقادیر آرایه جدید [۳, ۶, ۹] خواهد بود. سپس با اجرای متد filter() تنها مقادیر [۳, ۹] در نظر گرفته میشود و reduce() این دو مقدار را با هم جمع میکند و عدد ۱۲ را برمیگرداند.
با توجه به تمامی توضیحاتی که داده شد، میتوان به راحتی پی برد که map() بسیار ایدهآلتر از متد forEach() است. نه تنها آرایه اصلی را دستنخورده نگه میدارد، بلکه از لحاظ عملکرد نیز از سرعت بالایی برخوردار است. همچنین قابلیت ترکیب با سایر متدها را دارد که بسیار فوقالعاده به نظر میرسد.
اما با اینحال باز هم نمیتوان با قاطعیت گفت که حتما از map() استفاده کنید یا از forEach(). این موضوع کاملا برعهده شماست که از کدام یک در برنامهنویسی جاوا اسکریپت بهره بگیرید.
[button class=”github-btn” href=”http://frontcast.ir/course/javascript-advanced”]دوره جامع و پیشرفته جاوااسکریپت[/button]