البرمجة الوظيفية في JavaScript: من النظرية إلى التطبيق
البرمجة الوظيفية (FP) هي نموذج يعالج العمليات الحسابية كتقييم للدوال الرياضية ويتجنب تغيير الحالة والبيانات القابلة للتعديل. في JavaScript الحديثة، خاصة في بيئة Node.js، يزداد شيوع المنهج الوظيفي بسبب قابلية التنبؤ والاختبار والنمطية. في هذه المقالة، سنشرح المفاهيم الأساسية للبرمجة الوظيفية ونوضح كيفية تطبيقها في المشاريع الفعلية.
1. الدوال النقية وعدم وجود آثار جانبية
الدالة النقية هي دالة:
- تعيد دائمًا نفس النتيجة لنفس الوسائط (الحتمية).
- لا تسبب آثارًا جانبية (لا تعدل متغيرات خارجية، لا تُخرج بيانات، لا تصل إلى نظام الملفات).
مثال لدالة نقية:
// Pure function: adds two numbersconst add = (a, b) => a + b;
console.log(add(2, 3)); // 5console.log(add(2, 3)); // 5 — always the sameمثال لدالة غير نقية:
let counter = 0;const increment = () => { counter++; // Side effect: modifies an external variable return counter;};
console.log(increment()); // 1console.log(increment()); // 2 — different resultفي البرمجة الوظيفية، نسعى لكتابة دوال نقية فقط. وهذا يجعل الكود أسهل في الاختبار والتصحيح.
2. ثبات البيانات
الثبات يعني أن البيانات لا تتغير بعد الإنشاء. بدلاً من تعديل كائن موجود، ننشئ نسخة جديدة منه مع التغييرات اللازمة. في JavaScript، غالبًا ما يُستخدم عامل الانتشار (...) أو طرق Object.assign لهذا الغرض.
مثال لتحديث كائن بطريقة ثابتة:
const user = { name: 'Alice', age: 30 };
// Bad: mutationuser.age = 31;
// Good: create a new objectconst updatedUser = { ...user, age: 31 };
console.log(user); // { name: 'Alice', age: 30 }console.log(updatedUser); // { name: 'Alice', age: 31 }بالنسبة للمصفوفات، نستخدم أيضًا طرقًا ثابتة:
const numbers = [1, 2, 3];
// Mutation (bad)numbers.push(4);
// Immutable (good)const newNumbers = [...numbers, 4];
console.log(numbers); // [1, 2, 3]console.log(newNumbers); // [1, 2, 3, 4]3. دوال الرتبة العليا: map, filter, reduce
دوال الرتبة العليا هي دوال تأخذ دوالًا أخرى كوسائط أو تعيدها. الطرق الثلاث الأكثر شيوعًا في JavaScript هي map و filter و reduce. تتيح هذه الطرق معالجة المصفوفات دون حلقات أو تعديلات.
3.1. map() — تحويل مصفوفة
طريقة map تنشئ مصفوفة جديدة بتطبيق دالة على كل عنصر من المصفوفة الأصلية.
const prices = [100, 200, 300];const withTax = prices.map(price => price * 1.2);
console.log(withTax); // [120, 240, 360]// The original array has not changedconsole.log(prices); // [100, 200, 300]3.2. filter() — تصفية مصفوفة
طريقة filter تعيد مصفوفة جديدة تحتوي فقط على العناصر التي أعادت الدالة الممررة إليها القيمة true.
const ages = [15, 22, 18, 30, 12];const adults = ages.filter(age => age >= 18);
console.log(adults); // [22, 18, 30]3.3. reduce() — تجميع البيانات
طريقة reduce تختزل مصفوفة إلى قيمة واحدة (رقم، كائن، سلسلة نصية).
const numbers = [1, 2, 3, 4, 5];const sum = numbers.reduce((accumulator, current) => accumulator + current, 0);
console.log(sum); // 15من خلال الجمع بين هذه الطرق، يمكنك حل مشاكل معقدة بأسلوب وظيفي:
const users = [