Menu
العربية

Optional Chaining في جافا سكريبت: الوصول الآمن بـ ?.

كيف يتيح لك المعامل ?. الوصول إلى خصائص الكائنات المتداخلة والمصفوفات والدوال دون أن ينهار الكود عند مصادفة null أو undefined.

المشكلة: الوصول إلى الخصائص المتداخلة هش وغير آمن

الوصول إلى كائن متداخل يبدو أمرًا بسيطًا، لكن المشكلة تبدأ عندما يكون أحد المستويات غير موجود:

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

user.address قيمته undefined، ومحاولة قراءة .city من undefined ترمي خطأ TypeError: Cannot read properties of undefined. البيانات التي نتعامل معها في الواقع — من استجابات الـ API، وتحليل JSON، واستعلامات الـ DOM — مليئة بحقول قد تكون موجودة وقد لا تكون، وكتابة فحوصات دفاعية عند كل مستوى يُحوّل الكود إلى فوضى بسرعة:

const city = user && user.address && user.address.city;

مقروء نوعًا ما على مستويين، لكنه يتحوّل إلى كابوس عند أربعة مستويات. معامل ?. هو الحل الأنظف لهذه المشكلة.

معامل ?. يتوقف فورًا عند null و undefined

يعمل Optional Chaining على قراءة الخاصية فقط إذا لم تكن القيمة التي تسبقها null أو undefined. فإن كانت كذلك، يُرجع التعبير بأكمله القيمة undefined ويتوقف عن المتابعة.

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

السطر الأول يقرأ name بشكل طبيعي. السطر الثاني يصل إلى user.address، يجد القيمة undefined، فيتوقف فورًا — وبقية السلسلة لا تُنفَّذ إطلاقًا. أما السطر الثالث فكان سيرمي خطأً بدون ?. لأنه سيستدعي .toUpperCase() على undefined؛ لكن مع ?.، يتحوّل الناتج بهدوء إلى undefined.

الفكرة ببساطة: معامل ?. يقول "إذا كان ما قبلي يساوي null أو undefined، توقّف وأعِد undefined — وإلا فتابع السير."

يعمل مع المصفوفات واستدعاءات الدوال أيضًا

ثلاث صيغ، ونفس المبدأ:

index.js
Output
Click Run to see the output here.
  • ?.[...] للوصول إلى عناصر المصفوفات أو الخصائص الديناميكية.
  • ?.() لاستدعاء شيء قد يكون دالة وقد لا يكون.
  • ?.name للوصول العادي إلى الخصائص.

الثلاثة جميعها تعمل بنفس منطق التقييم القصير (short-circuit). مثلاً user?.notAMethod?.() لن يُطلق أي خطأ رغم أن notAMethod غير موجود أصلاً، لأن المعامل ?. الثاني يرى القيمة undefined فيتوقف مباشرة.

وهذا الأسلوب مفيد جداً مع دوال رد النداء (callbacks) الاختيارية:

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

لن تحتاج بعد الآن لكتابة if (typeof onDone === "function") قبل كل استدعاء.

null و undefined فقط هما ما يُفعّلان التوقف القصير

هذه النقطة بالذات يقع فيها كثيرون. معامل ?. يهتم بالقيم الـ nullish فقط، أي null و undefined لا غير. أما بقية القيم الزائفة (falsy) — مثل 0 و "" و false و NaN — فهي قيم صالحة تمامًا للاستمرار في السلسلة (بقدر ما يسمح به الـ autoboxing طبعًا):

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

قيمة data.count هي 0، وهي قيمة falsy لكنها ليست nullish، لذا يُنفَّذ ?.toFixed(2) عليها ويُعيد "0.00". قارن هذا بسلوك &&:

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

النسخة التي تستخدم && ترجع 0 لأن data.count قيمة falsy فيحصل short-circuit. أما النسخة التي تستخدم ?. فترجع "0.00" لأن 0 ليست قيمة nullish. إذا كنت فعلاً تريد التوقف عند 0، فالمعامل && هو الخيار الصحيح. أما إذا كنت تريد التوقف فقط عندما "تكون القيمة مفقودة"، فاستخدم ?..

موضع علامة ? يُحدث فرقاً

المعامل ?. يحمي القيمة التي تسبقه مباشرةً، لا التي تليه. لذلك ضعه عند المستوى الذي يُحتمل أن يكون مفقوداً:

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

كل هذه الطرق تعمل هنا لأنه لا يوجد شيء مفقود فعلياً. لكن إذا كان config.server من المحتمل أن يكون undefined، فستحتاج إلى config.server?.host — ووضع ?. قبل server لن يفيد، لأن المشكلة الحقيقية هي محاولة قراءة .host من كائن server غير موجود.

قاعدة ذهبية: ضع ?. في كل مستوى تكون فيه القيمة التي تسبقه قابلة فعلاً لأن تكون null أو undefined. أما نثر ?. على كل نقطة "تحسباً فقط" فهو يخفي الأخطاء ويجعل الكود مزدحماً بلا داعٍ.

لا يمكن الإسناد عبر ?.

معامل ?. في javascript للقراءة فقط. لا يمكنك استخدامه على يسار عملية الإسناد:

user?.address?.city = "باريس";   // SyntaxError

لو فكّرت في الأمر شوية، هتلاقيه منطقي — يعني إيه إنك تسند قيمة لخاصية على undefined؟ لو محتاج تضبط قيمة بس بشرط إن الكائن الأب يكون موجود فعلاً، اكتبها بالطريقة دي:

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

متى تستخدمه ومتى تتجنبه

معامل ?. يتألق عندما تكون القيمة اختيارية فعلاً:

  • استجابات الـ API التي قد تتضمن حقولاً معينة أو لا تتضمنها.
  • الوصول إلى عناصر الـ DOM: document.querySelector(".banner")?.remove().
  • دوال الـ callback التي قد لا تُمرَّر أصلاً: options.onError?.(err).
  • السلاسل المتتابعة في المكتبات حيث يمكن أن تكون النتائج الوسيطة null.

لكنه الأداة الخاطئة حين يُفترض أن تكون القيمة موجودة دائماً. استخدام ?. لإسكات الأخطاء في هذه الحالة يحوّل خطأً واضحاً يسهل تعقّبه ("TypeError في السطر 42") إلى خطأ صامت يظهر على شكل متغير قيمته undefined بشكل غامض في دالة أخرى بعيدة. إذا كانت القيمة يجب أن تكون موجودة، دع الكود يرمي الخطأ — فالـ stack trace يقدّم لك خدمة لا مشكلة.

مثال واقعي

استخراج قيمة من استجابة API قد تكون ناقصة:

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

كل ?. يتعامل مع طبقة واحدة من عدم اليقين. النتيجة أن avatarUrl تصبح undefined بشكل نظيف، وonClick?.() يستدعي المعالج فقط إن كان موجوداً فعلاً.

لاحظ وجود ?? في سطر displayName — هذا هو النصف الآخر من النمط. ?. يعطيك undefined عندما يكون شيء ما مفقوداً، بينما ?? يتيح لك وضع قيمة افتراضية دون أن يتعثر بقيم falsy المشروعة مثل 0 أو "".

التالي: Nullish Coalescing Operator

?. و?? هما نفس الفكرة مطبَّقة على مشكلتين مختلفتين: كلاهما يعامل null وundefined على أنهما "مفقود" ويترك بقية قيم falsy وشأنها. في الجزء التالي، سنرى كيف يمنحك ?? قيماً افتراضية منطقية — ولماذا ظل || يخطئ في هذا الأمر بصمت منذ سنوات.

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

ما وظيفة المعامل ?. في جافا سكريبت؟

المعامل ?. يصل إلى الخاصية أو عنصر المصفوفة أو الدالة فقط إذا لم تكن القيمة التي قبله null أو undefined. وإذا كانت كذلك، فإن التعبير بأكمله يتوقف (short-circuit) ويُرجع undefined بدلاً من رمي خطأ. مثلاً user?.address?.city لن ينهار حتى لو كان user أو address غير موجود.

متى نستخدم Optional Chaining في جافا سكريبت؟

استخدم ?. حين تكون القيمة مسموح لها فعلياً بأن تكون غائبة — مثل استجابات API فيها حقول اختيارية، أو عمليات بحث في DOM قد لا تُرجع عنصراً، أو callbacks قد تُمرَّر وقد لا تُمرَّر. لا تستخدمه لتغطية أخطاء في كود يفترض أن القيمة موجودة دائماً؛ في تلك الحالات غياب القيمة هو إشارة مهمة تريد رؤيتها لا إخفاءها.

ما الفرق بين ?. و && في جافا سكريبت؟

كلاهما يعطي نفس النتيجة في أغلب الحالات، لكن ?. يتوقف فقط عند null أو undefined، بينما && يتوقف عند أي قيمة falsy بما فيها 0 و'' وfalse. فمثلاً obj && obj.count يُرجع 0 عندما يكون count يساوي 0، أما obj?.count فيُرجع 0 أيضاً لكنه يتوقف فقط عند القيم nullish، وهذا أدق في الغالب.

هل يمكن استخدام Optional Chaining مع المصفوفات واستدعاء الدوال؟

نعم. الصيغة arr?.[0] تقرأ عنصراً من المصفوفة بأمان، وfn?.() تستدعي الدالة فقط إذا كانت موجودة. وكلاهما يتبع نفس القاعدة: إذا كانت القيمة قبل ?. هي null أو undefined، فإن التعبير يُرجع undefined ولا يتم تنفيذ شيء بعده.

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

ابدأ الآن