إضافة حديثة
هذه إضافة حديثة للغة. قد تحتاج المتصفحات القديمة إلى عمليات تعبئة متعددة.
التسلسل الاختياري ?.
هي طريقة آمنة للوصول إلى خصائص الكائنات المتداخلة، حتى في حالة عدم وجود خاصية وسيطة.
إذا كنت قد بدأت للتو في قراءة البرنامج التعليمي وتعلم JavaScript، فربما لم تطرقك المشكلة بعد، ولكنها شائعة جدًا.
على سبيل المثال، لنفترض أن لدينا كائنات user
تحتوي على معلومات حول مستخدمينا.
معظم مستخدمينا لديهم عناوين في خاصية user.address
، مع الشارع user.address.street
، لكن البعض لم يقدمها.
في مثل هذه الحالة، عندما نحاول الحصول على user.address.street
، ويصادف أن المستخدم ليس لديه عنوان، نحصل على خطأ:
دع المستخدم = {}؛ // مستخدم بدون خاصية "العنوان". تنبيه (user.address.street)؛ // خطأ!
هذه هي النتيجة المتوقعة. تعمل جافا سكريبت بهذه الطريقة. نظرًا لأن user.address
undefined
، فقد فشلت محاولة الحصول على user.address.street
مع حدوث خطأ.
في العديد من الحالات العملية، نفضل الحصول على undefined
بدلاً من الخطأ هنا (بمعنى "لا يوجد شارع").
…ومثال آخر. في تطوير الويب، يمكننا الحصول على كائن يتوافق مع عنصر صفحة ويب باستخدام استدعاء أسلوب خاص، مثل document.querySelector('.elem')
، ويعيد null
عندما لا يكون هناك مثل هذا العنصر.
// document.querySelector('.elem') يكون فارغًا إذا لم يكن هناك عنصر Let html = document.querySelector('.elem').innerHTML; // خطأ إذا كان فارغًا
مرة أخرى، إذا كان العنصر غير موجود، فسوف نحصل على خطأ أثناء الوصول إلى خاصية .innerHTML
الخاصة بـ null
. وفي بعض الحالات، عندما يكون غياب العنصر أمرًا طبيعيًا، نرغب في تجنب الخطأ ونقبل فقط html = null
كنتيجة.
كيف يمكننا أن نفعل هذا؟
الحل الواضح هو التحقق من القيمة باستخدام if
أو العامل الشرطي ?
، قبل الوصول إلى ممتلكاته، مثل هذا:
دع المستخدم = {}؛ تنبيه (user.address؟ user.address.street: غير محدد)؛
إنه يعمل، ليس هناك خطأ... لكنه غير أنيق تمامًا. كما ترون، يظهر "user.address"
مرتين في الكود.
إليك كيف سيبدو الأمر نفسه مع document.querySelector
:
دع html = document.querySelector('.elem')؟ document.querySelector('.elem').innerHTML : null;
يمكننا أن نرى أن عنصر البحث document.querySelector('.elem')
يتم استدعاؤه بالفعل مرتين هنا. ليس جيدا.
بالنسبة للخصائص المتداخلة بشكل أعمق، يصبح الأمر أكثر قبحًا، حيث يلزم المزيد من التكرار.
على سبيل المثال، لنحصل على user.address.street.name
بطريقة مماثلة.
دع المستخدم = {}؛ // المستخدم ليس لديه عنوان تنبيه (user.address ? user.address.street ? user.address.street.name : null : null);
هذا أمر فظيع للغاية، بل قد يواجه المرء مشاكل في فهم مثل هذا الرمز.
هناك طريقة أفضل قليلاً لكتابتها باستخدام عامل التشغيل &&
:
دع المستخدم = {}؛ // المستخدم ليس لديه عنوان تنبيه (user.address && user.address.street && user.address.street.name ); // غير محدد (لا يوجد خطأ)
ويضمن تحديد المسار بالكامل إلى الخاصية وجود جميع المكونات (إذا لم يكن الأمر كذلك، سيتوقف التقييم)، ولكنه أيضًا ليس مثاليًا.
كما ترون، لا تزال أسماء الخصائص مكررة في الكود. على سبيل المثال، في الكود أعلاه، يظهر user.address
ثلاث مرات.
لهذا السبب التسلسل الاختياري ?.
تمت إضافته إلى اللغة. لحل هذه المشكلة مرة واحدة وإلى الأبد!
التسلسل الاختياري ?.
يوقف التقييم إذا كانت القيمة قبل ?.
undefined
أو null
ويعود undefined
.
علاوة على ذلك، في هذه المقالة، للإيجاز، سنقول أن شيئًا ما "موجود" إذا لم يكن null
أو undefined
.
بمعنى آخر، value?.prop
:
يعمل كقيمة value.prop
، إذا كانت value
موجودة،
خلاف ذلك (عندما تكون value
undefined/null
) فإنها تُرجع undefined
.
إليك الطريقة الآمنة للوصول إلى user.address.street
باستخدام ?.
:
دع المستخدم = {}؛ // المستخدم ليس لديه عنوان تنبيه (المستخدم؟.العنوان؟.الشارع)؛ // غير محدد (لا يوجد خطأ)
الكود قصير ونظيف، ولا يوجد أي تكرار على الإطلاق.
فيما يلي مثال مع document.querySelector
:
Let html = document.querySelector('.elem')?.innerHTML; // سيكون غير محدد، إذا لم يكن هناك عنصر
قراءة العنوان باستخدام user?.address
تعمل حتى في حالة عدم وجود كائن user
:
دع المستخدم = فارغ؛ تنبيه (المستخدم؟.العنوان)؛ // غير محدد تنبيه (المستخدم؟.address.street)؛ // غير محدد
يرجى ملاحظة: ?.
بناء الجملة يجعل القيمة التي تسبقها اختيارية، ولكن ليس أبعد من ذلك.
على سبيل المثال في user?.address.street.name
?.
يسمح user
بأن يكون null/undefined
بأمان (ويُرجع undefined
في هذه الحالة)، ولكن هذا خاص بـ user
. يتم الوصول إلى المزيد من العقارات بطريقة منتظمة. إذا أردنا أن يكون بعضها اختياريًا، فسنحتاج إلى استبدال المزيد .
مع ?.
.
لا تفرط في استخدام التسلسل الاختياري
ينبغي لنا أن نستخدم ?.
فقط عندما يكون من المقبول عدم وجود شيء ما.
على سبيل المثال، إذا كان يجب أن يكون كائن user
موجودًا وفقًا لمنطق الكود الخاص بنا، ولكن address
اختياري، فيجب علينا كتابة user.address?.street
، ولكن ليس user?.address?.street
.
بعد ذلك، إذا كان user
غير محدد، فسنرى خطأ برمجيًا بشأنه وسنقوم بإصلاحه. وإلا إذا أفرطنا في الاستخدام ?.
، يمكن إسكات أخطاء الترميز عندما لا يكون ذلك مناسبًا، ويصبح تصحيح الأخطاء أكثر صعوبة.
المتغير قبل ?.
يجب أن يعلن
إذا لم يكن هناك user
متغير على الإطلاق، فسيؤدي user?.anything
إلى حدوث خطأ:
// خطأ مرجعي: لم يتم تعريف المستخدم المستخدم؟.العنوان؛
يجب الإعلان عن المتغير (على سبيل المثال let/const/var user
أو كمعلمة دالة). يعمل التسلسل الاختياري فقط للمتغيرات المعلنة.
وكما قيل من قبل، فإن ?.
يوقف التقييم ("دوائر كهربائية قصيرة") على الفور في حالة عدم وجود الجزء الأيسر.
لذا، إذا كان هناك أي استدعاءات أو عمليات وظيفية أخرى على يمين ?.
، لن يتم صنعهم.
على سبيل المثال:
دع المستخدم = فارغ؛ دع س = 0؛ المستخدم؟.sayHi(x++); // لا يوجد "مستخدم"، لذلك لا يصل التنفيذ إلى استدعاء sayHi وx++ تنبيه (خ)؛ // 0، القيمة غير متزايدة
التسلسل الاختياري ?.
ليس عامل تشغيل، ولكنه بناء جملة خاص، يعمل أيضًا مع الوظائف والأقواس المربعة.
على سبيل المثال، يتم استخدام ?.()
لاستدعاء دالة قد لا تكون موجودة.
في الكود أدناه، بعض مستخدمينا لديهم طريقة admin
، والبعض الآخر لا:
السماح لمسؤول المستخدم = { مسؤل() { تنبيه("أنا مسؤول"); } }; دع userGuest = {}; userAdmin.admin?.(); // أنا المشرف userGuest.admin?.(); // لا يحدث شيء (لا توجد طريقة كهذه)
هنا، في كلا السطرين، نستخدم أولاً النقطة ( userAdmin.admin
) للحصول على خاصية admin
، لأننا نفترض أن كائن user
موجود، لذا يمكن قراءته بأمان.
ثم يتحقق ?.()
من الجزء الأيسر: إذا كانت وظيفة admin
موجودة، فسيتم تشغيلها (وهذا ينطبق على userAdmin
). بخلاف ذلك (بالنسبة لـ userGuest
) سيتوقف التقييم بدون أخطاء.
يعمل بناء الجملة ?.[]
أيضًا، إذا أردنا استخدام الأقواس []
للوصول إلى الخصائص بدلاً من النقطة .
. كما هو الحال في الحالات السابقة، فهو يسمح بقراءة خاصية بأمان من كائن قد لا يكون موجودًا.
دع المفتاح = "الاسم الأول"؛ دع المستخدم 1 = { الاسم الأول: "جون" }; دع user2 = null; تنبيه(user1?.[مفتاح]); // جون تنبيه (user2؟.[مفتاح])؛ // غير محدد
كما يمكننا استخدام ?.
مع delete
:
حذف المستخدم؟.الاسم؛ // احذف اسم المستخدم إذا كان المستخدم موجودًا
يمكننا استخدام ?.
للقراءة والحذف الآمن، ولكن ليس الكتابة
التسلسل الاختياري ?.
ليس له فائدة على الجانب الأيسر من المهمة.
على سبيل المثال:
دع المستخدم = فارغ؛ المستخدم؟.اسم = "جون"; // خطأ، لا يعمل // لأنه يتم تقييمه إلى: غير محدد = "جون"
التسلسل الاختياري ?.
النحو له ثلاثة أشكال:
obj?.prop
- يُرجع obj.prop
في حالة وجود obj
، وإلا فإنه undefined
.
obj?.[prop]
– يُرجع obj[prop]
في حالة وجود obj
، وإلا فإنه undefined
.
obj.method?.()
- يستدعي obj.method()
في حالة وجود obj.method
، وإلا فإنه يُرجع undefined
.
كما نرى، جميعها واضحة وسهلة الاستخدام. ال ?.
يتحقق من الجزء الأيسر بحثًا عن null/undefined
ويسمح بمواصلة التقييم إذا لم يكن الأمر كذلك.
سلسلة ?.
يسمح بالوصول بأمان إلى الخصائص المتداخلة.
ومع ذلك، ينبغي لنا أن نطبق ?.
بعناية، فقط عندما يكون من المقبول، وفقًا لمنطق الكود الخاص بنا، أن الجزء الأيسر غير موجود. حتى لا يخفي عنا الأخطاء البرمجية في حال حدوثها.