Menu
العربية
جرّب في Playground

القيم الافتراضية للمعاملات في JavaScript

تعرّف على طريقة عمل المعاملات في JavaScript: كيف تضبط قيمة افتراضية، متى تُفعَّل، الفرق بين parameter و argument، وأخطاء شائعة مع null.

الفرق بين parameter و argument

كلمتان يحصل فيهما خلط دائم. الـ parameter (المعامل) هو الاسم الذي تكتبه عند تعريف الدالة، أما الـ argument (الوسيط) فهو القيمة الفعلية التي تمرّرها عند استدعائها. نفس الفكرة تمامًا الموجودة في Python ومعظم اللغات الأخرى:

index.js
Output
Click Run to see the output here.

a و b هما المعاملان (parameters)، أما 2 و 3 فهما الوسيطتان (arguments). في جافا سكريبت يتم الربط بينهما حسب الترتيب: أول وسيطة تذهب إلى أول معامل، وهكذا. الفرق بين parameter و argument قد يبدو بسيطًا، لكنه المصطلح الذي ستراه في رسائل الأخطاء وفي توثيق MDN.

جافا سكريبت متساهلة مع عدد المعاملات

على عكس كثير من لغات البرمجة، لا تهتم جافا سكريبت إذا مرّرت وسيطات أقل أو أكثر من اللازم. المعاملات الناقصة تأخذ القيمة undefined، والزائدة يتم تجاهلها بصمت:

index.js
Output
Click Run to see the output here.

المكالمة الأولى تطبع مرحباً، Ada undefined — المعامل last لم تصله أي قيمة، فصار undefined، وقوالب السلاسل (template literals) تُدرج هذه القيمة دون أي مشكلة. لا خطأ، ولا حتى تحذير. هذا التساهل مفيد أحياناً، لكنه في أحيان أخرى يكون مصدراً للأخطاء، ومن هنا جاءت فكرة القيم الافتراضية للمعاملات.

كيفية تعيين قيمة افتراضية للمعامل

ضع علامة = متبوعةً بالقيمة بعد اسم المعامل مباشرة. إذا لم يُمرِّر المستدعي هذا الوسيط (أو مرَّر undefined)، تعمل القيمة الافتراضية تلقائياً:

index.js
Output
Click Run to see the output here.

الاستدعاءات الثلاثة كلها تعمل. الأول والثالث يُفعّلان القيمة الافتراضية، أما الثاني فيستخدم القيمة الممرَّرة. هذه صياغة ES6 — قبل 2015 كان لازم تكتب name = name || "friend" داخل جسم الدالة، وكانت هذه الطريقة تسبب مشاكل مع القيم الزائفة (falsy) مثل 0 و "".

القيمة الافتراضية يمكن أن تكون أي تعبير، وليس مجرد قيمة حرفية:

index.js
Output
Click Run to see the output here.

التعبير يُنفَّذ في كل استدعاء، وليس مرة واحدة عند تعريف الدالة. لذلك لا توجد هنا مشكلة القيم الافتراضية القابلة للتعديل كما في Python — كل استدعاء يحصل على new Date() جديدة.

القيمة الافتراضية تعمل مع undefined فقط

هذه هي القاعدة التي تُربك الكثيرين. القيم الافتراضية تُفعَّل فقط عندما تكون قيمة المعامل undefined، وليس عندما تكون قيمة falsy، و_ليس_ عندما تكون null:

index.js
Output
Click Run to see the output here.

فقط الاستدعاء الأخير هو الذي يستخدم "friend". تمرير null بشكل صريح يعني أنك تقول "القيمة هي null" — وJavaScript تأخذ كلامك على محمل الجد. نفس الأمر ينطبق على النصوص الفارغة والأصفار: هذه قيم فعلية، وليست معاملات مفقودة.

إذا أردت أن تعامل null مثل الحالة التي لا يُمرَّر فيها شيء، فعليك أن تتعامل مع ذلك بنفسك:

index.js
Output
Click Run to see the output here.

عامل ?? (المعروف بـ nullish coalescing) يعامل كلًا من null و undefined كأنهما "قيمة مفقودة". سنتعمق في هذا الموضوع في فصل لاحق.

قيمة افتراضية تعتمد على معامل آخر

يتم تقييم المعاملات بالترتيب من الأول إلى الأخير، ولهذا يمكن للقيمة الافتراضية لأي معامل أن تستفيد من أي معامل سبقه:

index.js
Output
Click Run to see the output here.

استدعاء بمُعامل واحد يُنتج مكعبًا، وباثنين يُعطيك مُجسَّمًا مربّع القاعدة. لكن انتبه لاتجاه القراءة — فلا يمكنك الإشارة إلى مُعامل لم يُصرَّح عنه بعد:

function bad(a = b, b = 1) {
  return a + b;
}

bad();   // ReferenceError: لا يمكن الوصول إلى 'b' قبل التهيئة

نفس قواعد المتغيرات المُعرَّفة بـ let: استخدامها قبل تعريفها يسبب خطأً.

دمج القيم الافتراضية مع تفكيك المعاملات (Destructuring)

يمكنك تفكيك المعاملات وإعطاءها قيمًا افتراضية في نفس الوقت. وهذا نمط شائع جدًا عند التعامل مع "كائن الخيارات" (options object) كوسيط للدالة:

index.js
Output
Click Run to see the output here.

هناك طبقتان من القيم الافتراضية تعملان معًا هنا. الطبقة الداخلية (role = "member"، active = true) تملأ الخصائص الناقصة، بينما = {} في الخارج تتكفّل بالحالة التي لا يمرّر فيها المستدعي أي وسيط أصلًا — فبدونها ستحاول createUser() تفكيك undefined وسيُرمى خطأ.

قد يبدو هذا النمط مزدحمًا للوهلة الأولى، لكنك ستراه في كل مكان داخل أكواد JavaScript الحديثة. وبمجرد أن تعتاد عينك على قراءة { ... } = {} باعتباره "خيارات مع قيم افتراضية"، سيمر عليك بسرعة.

تخطّي وسيط في المنتصف

لا تدعم JavaScript الوسائط المسمّاة كما في Python. لذا إذا أردت تخطّي معامل في المنتصف والاعتماد على قيمته الافتراضية، فعليك تمرير undefined صراحةً:

index.js
Output
Click Run to see the output here.

تمرير undefined للمعامل prefix يُبقي على القيمة الافتراضية، لكنه أسلوب قبيح. وإذا وجدت نفسك تكتب undefined في الاستدعاءات بشكل متكرر، فهذه إشارة واضحة إلى أن الوقت قد حان للانتقال إلى كائن خيارات (options object):

index.js
Output
Click Run to see the output here.

الآن صار المستدعي هو اللي يحدد اللي يبغى يغيّره بالاسم، وترتيب المعاملات ما يفرق.

القيم الافتراضية ما تُحسب ضمن length

تفصيلة صغيرة نادراً ما تهم، لكنها أحياناً تسبب لبس. خاصية length في الدالة ترجع عدد المعاملات اللي تسبق أول معامل له قيمة افتراضية:

index.js
Output
Click Run to see the output here.

المكتبات التي تفحص الدوال (مثل بعض أدوات الاختبار وحقن الاعتماديات DI) تعتمد على length لمعرفة عدد المعاملات "المطلوبة". جيد أن تعرف بوجود هذه القاعدة، لكن لا داعي لحفظها إلا إذا كنت تبني أداة من هذا النوع.

ما التالي: Rest و Spread

القيم الافتراضية تحل المشكلة حين تكون المعاملات معروفة مسبقًا. لكن أحيانًا لا تكون كذلك — قد تحتاج دالة تستقبل عددًا غير محدود من الوسائط، أو تمرر مجموعة من الوسائط إلى دالة أخرى. هنا يأتي دور ...rest ومعامل الانتشار spread، وهو ما سنتناوله بعد قليل.

الأسئلة الشائعة

كيف أضبط قيمة افتراضية لمعامل في JavaScript؟

تضع علامة = متبوعةً بالقيمة الافتراضية بعد اسم المعامل في توقيع الدالة، مثل: function greet(name = 'friend') { ... }. إذا استدعى أحدهم الدالة دون تمرير قيمة (أو مرّر undefined)، فستُستعمل القيمة الافتراضية. هذه الصيغة من ES6 وتعمل في كل بيئات JavaScript الحديثة.

ما الفرق بين parameters و arguments؟

الـ parameters هي الأسماء التي تكتبها في تعريف الدالة، فمثلاً function add(a, b) معاملاتها a و b. أما الـ arguments فهي القيم الفعلية التي تُمرَّر عند الاستدعاء، ففي add(2, 3) تكون الوسائط هي 2 و 3. التفريق بينهما مهم جداً حين تقرأ رسائل الأخطاء أو التوثيق.

هل تُفعَّل القيمة الافتراضية عند تمرير null؟

لا. القيمة الافتراضية تعمل فقط عندما يكون الوسيط undefined (أو غير ممرَّر أصلاً). أما تمرير null صراحةً فمعناه: "أنا أعطيتك قيمة، وهذه القيمة هي null"، فيتم تجاهل القيمة الافتراضية. هذه النقطة تُربك القادمين من لغات يُعامَل فيها null و undefined كشيء واحد.

هل يمكن أن تعتمد القيمة الافتراضية على معامل آخر؟

نعم. تُقيَّم المعاملات من اليسار إلى اليمين، لذا يمكن لمعامل لاحق أن يستعمل معاملاً سابقاً كقيمة افتراضية، مثل: function box(width, height = width). كما يمكنك استدعاء دالة داخل القيمة الافتراضية مثل function log(msg, time = Date.now())، ويُنفَّذ هذا التعبير من جديد في كل استدعاء.

تعلّم البرمجة مع Coddy

ابدأ الآن