بیاموزید که چگونه توابع داخلی جاوا اسکریپت مانند map() و filter()، و همچنین compose() و chain()، از مدیریت ظریفتری از آرایههای جاوا اسکریپت پشتیبانی میکنند.
- آرایههای جاوا اسکریپت سنتی
- برنامه نویسی عملکردی با آرایه
- forEach()
- Array.map()
- Array.filter()
- Array.reduce()
- توابع نوشتن
- نتیجهگیری
آرایههای جاوا اسکریپت روشی بسیار انعطافپذیر برای مدلسازی مجموعهها با استفاده از تکنیکهای برنامهنویسی عملکردی است. این مقاله شما را با استفاده از ابزارهایی مانند forEach()
، map()
و reduce()
برای آرایههای دارای سبک عملکردی آشنا میکند.
آرایه های جاوا اسکریپت سنتی
آرایههای جاوا اسکریپت میتوانند انواع ناهمگن را در خود نگه دارند، اندازه را تغییر دهند و به آسانی عناصر را وارد یا حذف کنند. روشهای سنتی مانند slice، splice و push/pop این کار را با عمل بر روی خود آرایه انجام میدهند و مجموعه را به روشی «مخرب» تغییر میدهند:
// Create an array with heterogeneous types:
let myArray = [10, "hello", true, { name: "Alice" }];
// Add to an array and change size on the fly:
myArray.push(42);
// Extract elements without modifying the original array:
let extracted = myArray.slice(1, 3);
برنامه نویسی کاربردی با آرایه ها
اگرچه آرایه های جاوا اسکریپت خارج از جعبه بسیار توانمند هستند، الگوی عملکردی وضوح و قابلیت نگهداری کد آرایه را بهبود می بخشد. به طور کلی، برنامه نویسی تابعی از توابع به عنوان عملگرهایی استفاده می کند که می توانند به آرایه ها منتقل شوند. این اجازه می دهد تا روی آرایه مانند سر روی نوار کار کنید، به جای حلقه های ضروری سنتی که به تفصیل آنچه را که قرار است رخ دهد، توصیف می کند.
بیایید به چند نمونه از کار با آرایه ها در پارادایم عملکردی نگاه کنیم.
forEach()
Array.forEach()
اولین مثال ما است. این به شما امکان می دهد تابعی را که به صورت مکرر عملیات دلخواه را روی عناصر انجام می دهد عبور دهید. این یک جایگزین رایج برای حلقه سنتی for
است:
myArray.forEach((element) => {
console.log("my element is: " + element);
})
در این مثال، ما فقط خروجی هر عنصر را به کنسول میدهیم. معادل در یک حلقه for
خواهد بود:
for (let i = 0; i < myArray.length; i++) {
console.log("my element is: " + myArray[i]);
}
متوجه خواهید شد که قطعات متحرک کمتری در نسخه کاربردی وجود دارد. به طور خاص، ما تکرار کننده (i
) را حذف می کنیم، که یک متغیر خارجی است که برای بیان منطق مکانیک استفاده می شود، نه بخشی از قصد واقعی. توجه داشته باشید که من پیشنهاد نمی کنم حلقه های for
جایی ندارند. آنها گاهی اوقات ابزار مناسبی هستند، اما اغلب، forEach()
یک رویکرد تمیزتر است.
برنامه نویسی کاربردی به عنوان یک فلسفه، “تغییر ناپذیری” را ترویج می کند. این بدان معنی است که دوست دارد از تغییر متغیرها اجتناب کند. در عوض، برنامهنویسی تابعی ترجیح میدهد یک متغیر موجود را بگیرد و آن را از طریق یک “خط لوله” (یک تابع یا توابع) عبور دهد که آن را به یک متغیر جدید تبدیل میکند و متغیر اصلی را همانطور که هست باقی میگذارد.
forEach
اغلب به این صورت استفاده می شود، اما اغلب به صورت “مخرب” نیز استفاده می شود، همانطور که در اینجا نشان داده شده است:
const numbers = [1, 2, 3, 4, 5];
numbers.forEach(function(number, index) {
if (number % 2 === 0) { // Check for even numbers
numbers.splice(index, 1); // Remove even numbers from the array
}
});
این مثال ممکن است خالص ترین شکل برنامه نویسی تابعی در نظر گرفته نشود، اما از ویژگی های عملکردی کلیدی مانند “توابع درجه اول” استفاده می کند. وقتی به یک تابع مرتبه اول اشاره می کنیم، منظورمان این است که از تابعی مانند هر مرجع دیگری استفاده می کنیم، در این مورد، با ارسال آن به عنوان آرگومان. طولانی و کوتاه این داستان این است که توابع می توانند به عنوان بسته های قابل حملی از عملکرد عمل کنند که برای انجام کارها به روش های قابل پیش بینی ارائه می شوند.
توجه داشته باشید که هنوز موارد زیادی وجود دارد که در آن حلقه for
قدیمی بهترین رویکرد است. به عنوان مثال، هنگام تکرار با عددی غیر از ۱، تکرار به عقب، و هنگام مدیریت سناریوهای پیچیده که نیاز به تکرار کننده های متعدد دارند.
Array.map()
به عملکردهایی که غیرمخرب هستند و از هرگونه «عوارض جانبی» دیگر جلوگیری میکنند، «عملکردهای خالص» گفته میشود. ما می توانیم از forEach
به این روش استفاده کنیم، اما تابع Array.map()
به طور خاص برای این منظور طراحی شده است. روی خود آرایه کار نمی کند، بلکه عملگر تابع را اجرا می کند و نتیجه را به صورت یک آرایه جدید برمی گرداند:
const bands = [
{ name: "Led Zeppelin", year: 1968 },
{ name: "Pink Floyd", year: 1965 },
{ name: "Queen", year: 1970 },
{ name: "The Clash", year: 1976 },
{ name: "The Ramones", year: 1974 },
{ name: "R.E.M.", year: 1980 },
];
const bandNames = bands.map(band => {
return band.name;
});
// bandNames is an array that has just the string band names
Array.map()
مکانیزم بسیار قدرتمندی برای تبدیل آرایه ها است. این به شما این توانایی را می دهد که تقریباً هر کاری را با یک آرایه به روشی تمیز انجام دهید. به ویژه، از پیچیدگی در تغییر آرایه اصلی جلوگیری می کند، جایی که کدهای دیگر ممکن است به روش های ناشناخته یا غیرمنتظره ای به آن وابسته باشند.
از سوی دیگر، مهم است که در نظر داشته باشید که Array.map()
همیشه یک کپی میسازد که پیامدهای عملکردی دارد. شما نمی خواهید از آن در آرایه های بسیار بزرگ استفاده کنید. گاهی اوقات، ملاحظات حافظه حکم می کند که از روش دیگری استفاده کنید.
این کار به این صورت است که هر آنچه که تابع ارائه شده برمی گرداند در آرایه جدید نگه داشته می شود. بنابراین، میتوانیم از نسخه برگشتدهنده خودکار یک تابع استفاده کنیم:
const bandNames = bands.map(band => band.name)
این رویکرد میتواند برای عملکردهای کوتاه بسیار تمیزتر باشد.
Array.filter()
Array.map()
آرایه ای با طول یکسان منبع را خروجی می دهد. اگر تابع چیزی را برنگرداند، آرایه خروجی در آن موقعیت برچسب undefined
خواهد داشت. برای ایجاد یک آرایه با طول متفاوت، می توانید از Array.filter()
استفاده کنید. در آن صورت، زمانی که آرگومان تابعی چیزی برنمی گرداند، آن عنصر از آرایه هدف حذف می شود:
const bands = [
{ name: "Led Zeppelin", year: 1968 },
{ name: "Pink Floyd", year: 1965 },
{ name: "Queen", year: 1970 },
{ name: "The Clash", year: 1976 },
{ name: "The Ramones", year: 1974 },
{ name: "R.E.M.", year: 1980 },
];
const seventiesBands = bands.filter(band => {
if (band.year >= 1970 && band.year < 1980) {
return band;
}
});
// seventiesBands is an array holding only those bands satisfying the condition (band.year >= 1970 && band.year < 1980)
در این مثال، آرایهای از اشیاء را میگیریم که گروههای راک و سال تشکیل آنها را نگه میدارند و سپس از bands.filter()
برای ارائه تابعی استفاده میکنیم که فقط یک آرایه جدید را به ما میدهد. گروه های دهه ۱۹۷۰
Array.reduce()
گاهی اوقات، باید یک آرایه کامل را بردارید و آن را به یک مقدار تبدیل کنید. برای آن، می توانید از Array.reduce
:
استفاده کنید
// same band array as source
const earliestBand = bands.reduce((earliestSoFar, band) => {
return band.year < earliestSoFar.year ? band : earliestSoFar;
}, { year: Infinity }); // Start with a band in the infinitely distant future
console.log(earliestBand.name); // outputs “Pink Floyd”
تابع ارسال شده به reduce()
دارای دو آرگومان است: “accumulator” و عنصر فعلی. انباشته کننده چیزی است که در نهایت بازگردانده می شود و وضعیت خود را در هر تکرار حفظ می کند و به شما امکان می دهد همه چیز را در یک خروجی “جمع آوری” کنید.
عملکرد reduce
ابزاری بسیار کاربردی در مواقعی است که به آن نیاز دارید. به عنوان یک مثال سریع دیگر، بگویید که میخواهید رشتهای حاوی تمام نامهای باند در یک رشته باشد. می توانید این کار را انجام دهید:
const allBandNames = bands.reduce((accumulator, band) => {
return accumulator + band.name + ", ";
}, ""); // Initial value is an empty string
توابع آهنگسازی
توابع داخلی که تاکنون دیدهاید، برای برنامهنویسی تابعی (و برنامهنویسی در خواهر و برادر بزرگ آن، برنامهنویسی واکنشی) اساسی هستند. اکنون، بیایید ایده پیوند توابع با هم برای دستیابی به برخی از عملکردهای دلخواه را در نظر بگیریم.
دو مورد از اساسی ترین و مهم ترین توابع پیوند عبارتند از compose()
و chain()
. بسیاری از کتابخانه های کاربردی برنامه نویسی و ابزار شامل آنها هستند، اما پیاده سازی آنها نیز آسان است. این مثال بعدی به شما یک نگاه واضح به نحوه کار آنها می دهد:
const compose = (...fns) => (x) => fns.reduceRight((v, f) => f(v), x);
const chain = (...fns) => (xs) => xs.reduce((acc, x) => acc.concat(fns.reduceRight((v, f) => f(v), x)), []);
compose()
بسیاری از توابع را با هم ترکیب می کند، به طوری که خروجی هر تابع از راست به چپ (بر اساس ترتیب آنها در انتقال به تابع) به تابع بعدی وارد می شود. chain()
همین کار را انجام می دهد، اما از چپ به راست.
این توابع همچنین به شما نگاهی به reduceRight()
میدهند، تصویر آینهای از reduce()
، که قبلاً دیدهاید. تابع reduceRight()
به شما امکان میدهد با رفتن به عقب در میان آرگومانهای عملکردی، جمعآوری کنید.
توابع compose()
و chain()
مختص آرایه ها نیستند، اما می توان از آنها استفاده کرد. در اینجا یک مثال ساده از استفاده از compose()
با آرایه آورده شده است:
const numbers = [1, 4, 2, 8, 5, 7];
// Define reusable higher-order functions:
const findEvenNumbers = arr => arr.filter(n => n % 2 === 0);
const doubleNumbers = arr => arr.map(n => n * 2);
const sortNumbers = arr => arr.sort((a, b) => a - b);
// Compose functions to create complex transformations:
const processNumbers = compose(sortNumbers, doubleNumbers, findEvenNumbers);
const processedNumbers = processNumbers(numbers);
console.log(processedNumbers); // Output: [4, 8, 16]
نتیجه گیری
ترتیب توابع برای برنامه نویسی عملکردی و واکنشی مرکزی است. این به شما امکان می دهد تا از توابع مجدد استفاده کرده و آن ها را در توابع جدید ترکیب کنید. در اصل، میتوانید توابع ترکیبی را تعریف کنید که از قابلیتهای دیگر متمرکزتر تشکیل شدهاند. این از نظر مفهومی شبیه به این است که یک برنامه نویس شی گرا در مورد ترکیب برنامه ها از اشیا فکر می کند.
از آنجا که توابع کار خود را به روشی مینیمالیستی بیان می کنند – با ورودی و خروجی کل سطح API آنها – یک رویکرد فوق العاده تمیز ارائه می دهند. البته، با پیچیدهتر شدن، مقداری از این وضوح را از دست میدهید. حتی توابع compose()
و chain()
برخی از ظرافت توابع مستقیم را پشت سر می گذارند.
به طور کلی، مدیریت توابع آرایهای که در اینجا دیدهایم، با استفاده از توابع داخلی جاوا اسکریپت مانند map()
و filter()
، یک کاربرد عالی است. قدرت برنامه نویسی کاربردی
پست های مرتبط
برنامه نویسی کاربردی با آرایه های جاوا اسکریپت
برنامه نویسی کاربردی با آرایه های جاوا اسکریپت
برنامه نویسی کاربردی با آرایه های جاوا اسکریپت