دليل تعلم جافاسكربت التسلسل الاختياري ".?" للوصول لخاصيات كائن في جافاسكربت


Mohamed Lahlah

ملاحظة مهمة: هذه إضافة حديثة للغة، لذا قد تحتاج المتصفحات القديمة لترقيع هذا النقص.

التسلسل الاختياري .? هو طريقة مقاومة للأخطاء للوصول إلى خصائص الكائن المتداخلة، حتى إذا كانت الخاصية الوسيطة غير موجودة.

المشكلة

إذا كنت قد بدأت للتو في قراءة هذه السلسلة التعليمية وتتعلم جافاسكربت، فلربما لم تواجه هذه المشكلة بعد، لكنها شائعة جدًا.

فمثلًا، يمتلك بعض من المستخدمين لدينا يمتلكون عناوين، لكن هنالك قليل منهم لم يقدمها. لذا لا يمكننا قراءة user.address.street بأمان. هكذا:

let user = {}; // تحدث للمستخدم ‫user في حالة كان ليس لديه عنوان

alert(user.address.street); // خطأ!‫  

أو عند تطويرنا لموقع وِب، ونرغب في الحصول على معلومات حول عنصر ما في الصفحة، لكنه قد لا يكون موجودًا:

// إذا كان نتيجة ‫querySelector(...)‎ فارغًا
let html = document.querySelector('.my-element').innerHTML;

قبل ظهور "‎?.‎" في اللغة، كان يستخدم المعامل && للتغلب على المشكلة.

فمثلًا:

let user = {}; // إذا كان ‫user لا يملك عنوان

alert( user && user.address && user.address.street ); // undefined (أي ليس خطأً)

وكونها تتطلب كتابة طويلة للتأكد من وجود جميع المكونات، لذلك كان استخدامها مرهقًا.

تسلسل اختياري

التسلسل الاختياري "‎?.‎" يوقف التقييم ويعيد غير معرّف undefined إذا كان الجزء قبل "‎?.‎" غير معرّف undefined أو فارغ null.

**للإيجاز سنفترض في هذه المقالة أن شيئًا ما "موجود" إذا لم تكن القيمة "فارغة" null أو غير معرّفة undefined. **

إليك الطريقة الآمنة للوصول إلى user.address.street:

let user = {}; // إذا كان ‫user لا يملك عنوان

alert( user?.address?.street ); // undefined (ليس خطأً)

إن قراءة العنوان باستخدام هذه الطريقة user?.address ستعمل حتى ولو كان الكائن user غير موجود:

let user = null;

alert( user?.address ); // undefined
alert( user?.address.street ); // undefined

الرجاء ملاحظة أن: صياغة جملة .? تجعل القيمة الموجودة قبلها اختيارية، ولكن ليس القيمة الّتي تأتي بعدها.

في المثال أعلاه، تسمح التعليمة "user?.‎" للكائن user فقط بأن يكون غير معرف أو فارغ "null/undefined". من ناحية أخرى، إذا كان الكائن user موجودًا، فيجب أن يحتوي على خاصية user.address، وإلا فإن user?.address.street ستُعطي خطأ في النقطة الثانية.

ملاحظة: لا تفرط في استخدام التسلسل الاختياري. يجب أن نستخدم .? فقط في حالة عدم وجود شيئ ما.

فمثلًا، بحسب منطق الشيفرة خاصتنا يجب أن يكون الكائن user موجودًا، ولكن الخاصية address اختيارية، بهذه الحالة سيكون user?.address.street أفضل. لذلك، إذا حدث أن كان الكائن user غير معرف بسبب خطأ ما، فسنُعرف عنه ونصلحه. خلاف ذلك، يمكن إسكات أخطاء الترميز عندما لا يكون ذلك مناسبًا، ويصبح تصحيحها أكثر صعوبة.

ملاحظة: يجب التصريح عن المتغير الموجود قبل .?

إذا لم يكن هناك متغير user على الإطلاق، فإن التعليمة user?.anything ستؤدي حتمًا إلى حدوث خطأ:

// ReferenceError: إن المستخدم ‫user غير معرًف
user?.address;

يجب أن يكون هناك تعريف واضح للمتغير let / const / var user. لأن التسلسل الاختياري يعمل فقط للمتغيرات المصرح عنها.

اختيار الطريق الأقصر

كما قلنا سابقًا، ستوقف .? فورًا (أي سيحدث قصر في الدارة) إذا لم يكن الجزء الأيسر موجودًا.

لذلك، إذا كان هناك أي استدعاءات دوالّ أخرى أو آثار جانبية، فلن تحدث:

let user = null;
let x = 0;

user?.sayHi(x++); // لا يحدث شيئ

alert(x); // ‫0, لم تزداد القيمة

حالات أخرى ()?. و []?.

إن التسلسل الاختياري ‎.? ليس مُعامل، ولكنه طريقة معينة لصياغة تعليمة، يعمل أيضًا مع الدوالّ والأقواس المربعة.

فمثلًا، تُستخدم ().? لاستدعاء دالّة قد لا تكون موجودة.

نلاحظ في الشيفرة أدناه، أنه لدى بعض مستخدمينا التابع admin والبعض ليس لديه:

let user1 = {
  admin() {
    alert("I am admin");
  }
}

let user2 = {};

user1.admin?.(); // I am admin
user2.admin?.();

هنا، في كلا السطرين، نستخدم النقطة . أولًا للحصول على خاصية admin، لأن كائن المستخدم user يجب أن يكون موجودًا، لذا فهو آمن للقراءة منه. ثم يتحقق ().? من الجزء الأيسر: إذا كانت دالّة المسؤول موجودة، فستنفذ (على الكائن user1). وبخلاف ذلك (بالنسبة للكائن user2) يتوقف التقييم الشيفرة بدون أخطاء.

تعمل الصياغة [].? أيضًا، إذا أردنا استخدام الأقواس [] للوصول إلى الخصائص بدلًا من النقطة .. على غرار الحالات السابقة، فإنه يسمح بقراءة خاصية بأمان من كائن قد لا يكون موجودًا.

let user1 = {
  firstName: "John"
};

let user2 = null; // Imagine, we couldn't authorize the user

let key = "firstName";

alert( user1?.[key] ); // John
alert( user2?.[key] ); // undefined

alert( user1?.[key]?.something?.not?.existing); // undefined

كما يمكننا استخدام .? مع delete:

delete user?.name; // احذف المستخدم ‫user.name إذا كان موجودًا

ملاحظة: يمكننا استخدام .? للقراءة الآمنة والحذف، ولكن ليس الكتابة

التسلسل الاختياري .? ليس له أي فائدة في الجانب الأيسر من المهمة:

// فكرة الشيفرة أدناه أن تكتب قيمة ‫user.name إذا لم تكن موجودة

user?.name = "John"; // خطأ لن تعمل الشيفرة
// لأن الإسناد بين ‫undefined = "John"‎

الخلاصة

يتكون بناء جملة التسلسل الاختياري .? من ثلاثة أشكال:

  1. obj?.prop - ستُعيد obj.prop إذا كان الكائن obj موجودًا، وإلا ستعيد القيمة undefined.
  2. obj?.[prop]‎ - ستُعيد obj[prop]‎ إذا كان الكائن obj موجودًا، وإلا ستُعيد undefined.
  3. obj?.method()‎ - تستدعي obj.method()‎ إذا كان الكائن obj موجودًا، وإلا ستُعيد undefined.

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

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

ترجمة -وبتصرف- للفصل Optional chaining '?.' من كتاب The JavaScript language





تفاعل الأعضاء


لا توجد أيّة تعليقات بعد



يجب أن تكون عضوًا لدينا لتتمكّن من التعليق

انشاء حساب جديد

يستغرق التسجيل بضع ثوان فقط


سجّل حسابًا جديدًا

تسجيل الدخول

تملك حسابا مسجّلا بالفعل؟


سجّل دخولك الآن