تخطَّ إلى المحتوى

🟨 شرح جافاسكريبت

Closures ودوال الترتيب الأعلى — مستوى متقدم

الدرس 26 من 32· ⏱ 3 دقائق قراءة

Closures — ما وراء العدّاد البسيط

الإغلاق (Closure) هو دالة تحتفظ برابط إلى نطاقها الخارجي حتى بعد انتهاء تلك الدوال:

function createCounter(step = 1) {
  let count = 0;
  return {
    increment() { count += step; return count; },
    decrement() { count -= step; return count; },
    getValue() { return count; }
  };
}

const counter = createCounter(5);
counter.increment(); // 5
counter.increment(); // 10

count خاصّة — لا يمكن الوصول إليها إلا عبر الدوال المُرجَعة.

نمط الوحدة (Module Pattern)

const UserModule = (function() {
  const users = [];  // خاصة

  function addUser(name) {
    users.push({ id: users.length + 1, name });
  }

  function getUsers() {
    return [...users]; // نسخة وليس المرجع الأصلي
  }

  return { addUser, getUsers };
})();

UserModule.addUser("أحمد");
UserModule.addUser("سارة");
console.log(UserModule.getUsers()); // [{id:1,name:"أحمد"},...]
console.log(UserModule.users); // undefined! (خاص)

Currying — تطبيق جزئي للدوال

تحويل دالة تأخذ وسائط متعددة إلى سلسلة دوال تأكل وسيطًا واحدًا:

function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return (...more) => curried(...args, ...more);
  };
}

const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);

curriedAdd(1)(2)(3); // 6
const add5 = curriedAdd(2, 3); // دالة تنتظر الوسيط الأخير
add5(10); // 15

Memoization — حفظ نتائج الدوال

function memoize(fn) {
  const cache = new Map();
  return function(...args) {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn.apply(this, args);
    cache.set(key, result);
    return result;
  };
}

const fib = memoize(function(n) {
  if (n < 2) return n;
  return fib(n - 1) + fib(n - 2);
});

fib(40); // سريع جدًا بفضل التخزين المؤقت

المشكلة الكلاسيكية: Closures في الحلقات

// الكل يطبع 3
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}

// الحل بـ let (نطاق كتلة)
for (let i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100); // 0, 1, 2
}

// الحل القديم بـ IIFE
for (var i = 0; i < 3; i++) {
  (function(j) {
    setTimeout(() => console.log(j), 100); // 0, 1, 2
  })(i);
}

دوال الترتيب الأعلى (Higher-Order Functions)

دالة تستقبل دالة أو تُرجع دالة:

// تستقبل دالة
[1, 2, 3].map(x => x * 2);

// تُرجع دالة
function createMultiplier(factor) {
  return number => number * factor;
}

const double = createMultiplier(2);
const triple = createMultiplier(3);
double(5); // 10
triple(5); // 15

compose — تركيب الدوال

function compose(...fns) {
  return x => fns.reduceRight((acc, fn) => fn(acc), x);
}

const add2 = x => x + 2;
const multiply3 = x => x * 3;
const toString = x => `النتيجة: ${x}`;

const compute = compose(toString, multiply3, add2);
compute(5); // "النتيجة: 21" (5+2=7 → 7*3=21 → "النتيجة: 21")

⚠️ closures تستهلك ذاكرة — لا تفرط في إنشائها داخل حلقات ضخمة.

🎯 التالي: معالجة الأخطاء والتصحيح — مستوى متقدم.

شرح Closures ودوال الترتيب الأعلى — مستوى متقدم — جافاسكريبت بالعربي
Closures ودوال الترتيب الأعلى — مستوى متقدمجافاسكريبت بالعربي · The Code Fix

📚 لمزيد من التعمّق في جافاسكريبت، راجِع توثيق JavaScript على MDN.

هل كان هذا الدرس مفيدًا؟