البرمجة الوظيفية في JavaScript: من الألف إلى الياء
البرمجة الوظيفية (FP) هي نموذج ينظر إلى العمليات الحسابية على أنها حساب دوال رياضية ويتجنب تغيير الحالة والبيانات القابلة للتغيير. في سياق JavaScript (Node.js)، تصبح البرمجة الوظيفية أداة قوية لإنشاء كود قابل للتنبؤ والاختبار والتوسع. في هذه المقالة، سنشرح المفاهيم الأساسية للبرمجة الوظيفية من الأساسيات إلى التقنيات المتقدمة، مع دعم النظرية بأمثلة عملية.
الأساسيات: الدوال النقية والثبات
في جوهر البرمجة الوظيفية، هناك مبدآن: الدوال النقية والثبات (عدم قابلية البيانات للتغيير).
الدالة النقية تعيد دائمًا نفس النتيجة لنفس الوسائط وليس لها آثار جانبية (لا تغير المتغيرات الخارجية، لا تطبع في وحدة التحكم، لا تقوم باستدعاءات API).
// دالة نقيةconst add = (a, b) => a + b;
// دالة غير نقية (تأثير جانبي - تغيير متغير خارجي)let counter = 0;const increment = () => counter++;الثبات يعني أن البيانات لا يتم تغييرها مباشرة، بل يتم إنشاء نسخ جديدة منها بالتعديلات المطلوبة. في JavaScript، يتم استخدام دوال تعيد كائنات/مصفوفات جديدة.
// تحديث ثابت لكائنconst user = { name: 'Alice', age: 30 };const updatedUser = { ...user, age: 31 }; // إنشاء كائن جديد
// تحديث ثابت لمصفوفةconst numbers = [1, 2, 3];const newNumbers = [...numbers, 4]; // [1, 2, 3, 4]الإغلاقات والدوال عالية الترتيب
الإغلاق هو دالة "تتذكر" بيئتها المعجمية حتى بعد انتهاء تنفيذ الدالة الخارجية. هذا هو أساس العديد من أنماط البرمجة الوظيفية.
const createMultiplier = (factor) => { // الدالة الداخلية - إغلاق return (number) => number * factor;};
const double = createMultiplier(2);console.log(double(5)); // 10الدوال عالية الترتيب هي دوال تقبل دوال أخرى كوسائط أو تعيدها. الأمثلة الكلاسيكية هي map وfilter وreduce.
const numbers = [1, 2, 3, 4, 5];
// map - تحويل كل عنصرconst doubled = numbers.map(n => n * 2); // [2, 4, 6, 8, 10]
// filter - اختيار العناصر حسب الشرطconst even = numbers.filter(n => n % 2 === 0); // [2, 4]
// reduce - طي المصفوفة إلى قيمة واحدةconst sum = numbers.reduce((acc, n) => acc + n, 0); // 15تركيب الدوال والكاري
تركيب الدوال هو دمج عدة دوال في دالة واحدة، حيث يتم تمرير نتيجة دالة كمدخل للدالة التالية. هذا يسمح ببناء عمليات معقدة من كتل بسيطة قابلة لإعادة الاستخدام.
const compose = (...fns) => (x) => fns.reduceRight((acc, fn) => fn(acc), x);
const trim = (str) => str.trim();const toLowerCase = (str) => str.toLowerCase();const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
const processName = compose(capitalize, toLowerCase, trim);console.log(processName(' JOHN DOE ')); // 'John doe'الكاري هو تحويل دالة ذات وسائط متعددة إلى سلسلة من الدوال، كل منها يقبل وسيطة واحدة. هذا يبسط التطبيق الجزئي والتركيب.
// دالة عاديةconst multiply = (a, b, c) => a * b * c;
// النسخة المُكَرْيَةconst curriedMultiply = (a) => (b) => (c) => a * b * c;
console.log(curriedMultiply(2)(3)(4)); // 24
// تطبيق جزئيconst multiplyBy2 = curriedMultiply(2);const multiplyBy2And3 = multiplyBy2(3);console.log(multiplyBy2And3(5)); // 30