الدوال العادية في لغة JavaScript


يا هلا ومرحباً! اليوم بنتكلم عن أساسيات أي لغة برمجة: الدوال العادية في JavaScript. بسيطة ومباشرة، وهيا بينا نفهمها.

إيش هي الدالة (Function) أصلاً؟

ببساطة، الدالة هي مجموعة من الأوامر البرمجية اللي بتنفذ مهمة معينة. بتكتبها مرة واحدة، وتقدر تستدعيها (تشغلها) أكثر من مرة لما تحتاجها. كأنها آلة صغيرة بتسوي لك شغلة معينة.

1. تعريف الدالة (Function Declaration)

هذي الطريقة الأشهر والأسهل لتعريف دالة. تبدأ بكلمة function، بعدها اسم الدالة، بعدين أقواس () للمدخلات (parameters) إذا فيه، وبعدين أقواس معقوفة {} للكود اللي جوا الدالة.

function greetUser(name) {
  return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">أهلاً بك يا ${name}!</code>;
}

// كيف تستدعيها؟
console.log(greetUser('محمد')); // الناتج: أهلاً بك يا محمد!
console.log(greetUser('سارة'));  // الناتج: أهلاً بك يا سارة!

ملاحظة: الدوال اللي تعرفها بهالطريقة تقدر تستدعيها قبل ما تعرفها في الكود (وهذا يسمونه Hoisting)، لكن الأفضل دايماً تعرفها قبل الاستدعاء عشان الكود يكون أوضح.

2. تعبير الدالة (Function Expression)

هنا بنعرف الدالة كقيمة لمتغير (variable). يعني كأنك بتعطي الدالة لمتغير. ممكن تكون الدالة هذي لها اسم أو تكون مجهولة الاسم (Anonymous Function).

دالة مجهولة الاسم (Anonymous Function)

هذي الأكثر شيوعاً في الـ Function Expression. ما لها اسم، وبتحطها مباشرة في متغير.

const sayHello = function(userName) {
  return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">مرحباً يا ${userName}! كيف حالك؟</code>;
};

console.log(sayHello('علي')); // الناتج: مرحباً يا علي! كيف حالك؟

دالة مسماة (Named Function Expression)

نادر ما تستخدمها، لكن ممكن تعطي اسم للدالة حتى لو كانت تعبير. فايدتها في الـ debugging أو لو الدالة بتحتاج تستدعي نفسها (Recursion).

const factorial = function calculateFactorial(n) {
  if (n === 0) {
    return 1;
  }
  return n * calculateFactorial(n - 1); // هنا الدالة تستدعي نفسها باسمها الداخلي
};

console.log(factorial(5)); // الناتج: 120 (5*4*3*2*1)
// console.log(calculateFactorial(3)); // خطأ! الاسم calculateFactorial غير معروف خارج الدالة

ملاحظة: الدوال المعرفة كـ Function Expression (سواء كانت مسماة أو مجهولة) ما تدعم Hoisting. يعني لازم تعرفها قبل ما تستدعيها.

الكلمة المفتاحية this في الدوال العادية

هذي نقطة مهمة وتلخبط كثير! في الدوال العادية، قيمة this تتحدد بناءً على كيف تم استدعاء الدالة (Invocation Context)، مو مكان تعريفها.

  • إذا استدعيتها كـ طريقة (method) لكائن (object)، فـ this بتكون هي الكائن نفسه.
  • إذا استدعيتها بشكل مستقل (global context)، فـ this بتكون window في المتصفح أو undefined في الوضع الصارم (strict mode).
  • إذا استدعيتها باستخدام call() أو apply() أو bind()، فـ this بتكون القيمة اللي تمررها لها.
const person = {
  name: 'أحمد',
  sayName: function() {
    console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">اسمي هو ${this.name}</code>); // هنا this تشير للكائن person
  },
  greetLater: function() {
    setTimeout(function() {
      console.log(<code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">مرحباً من ${this.name}</code>); // هنا this تشير لـ window أو undefined (ليست person)
    }, 100);
  }
};

person.sayName(); // الناتج: اسمي هو أحمد
person.greetLater(); // الناتج: مرحباً من (undefined أو لا شيء، لأن this ليست person)

عشان تحل مشكلة this داخل setTimeout في المثال السابق، ممكن تستخدم bind() أو تخزن this في متغير قبلها، أو الأفضل: تستخدم Arrow Functions (اللي بنشرحها في درس ثاني) لأنها تحافظ على قيمة this من السياق اللي اتعرفت فيه.

الـ arguments Object

في الدوال العادية، فيه كائن خاص اسمه arguments تقدر تستخدمه عشان توصل لكل المدخلات (arguments) اللي تمررت للدالة، حتى لو ما عرفتها كـ parameters في تعريف الدالة.

function sumAllNumbers() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}

console.log(sumAllNumbers(1, 2, 3));       // الناتج: 6
console.log(sumAllNumbers(10, 20, 30, 40)); // الناتج: 100

ملاحظة: مع ظهور Rest Parameters (...args) في ES6، صار استخدام arguments أقل شيوعاً وأقل تفضيلاً لأن Rest Parameters تعطي مرونة أكبر وأداء أفضل.

متى تستخدم الدوال العادية؟

  • لما تحتاج سلوك this الديناميكي (يعني يتغير حسب طريقة الاستدعاء).
  • لما تحتاج تستخدم الكائن arguments (رغم إنه صار فيه بدائل أفضل).
  • لما تبغى تعرف دالة كـ Constructor Function لإنشاء كائنات جديدة باستخدام الكلمة new.

هذا كان كل شيء عن الدوال العادية في JavaScript! بسيطة ومهمة جداً. المرة الجاية بنتكلم عن الـ Arrow Functions اللي سهلت علينا أشياء كثيرة. إلى اللقاء!