تصميم لغة جافا سكريبت ليس دقيقًا بما فيه الكفاية، ويمكن أن تحدث أخطاء في العديد من الأماكن إذا لم تكن حذرًا.
على سبيل المثال، النظر في الوضع التالي.
الآن، نحن بحاجة إلى تحديد ما إذا كان الكائن العام myObj موجودًا أم لا، وإذا لم يكن موجودًا، فقم بإعلانه. الخوارزمية الموضحة باللغة الطبيعية هي كما يلي:
انسخ رمز الكود كما يلي:
إذا (myObj غير موجود) {
أعلن myObj؛
}
قد تعتقد أن كتابة هذا الرمز أمر سهل. ولكن في الواقع، فإن القضايا النحوية المعنية أكثر تعقيدًا بكثير مما نتصور. أشار Juriy Zaytsev إلى أن هناك أكثر من 50 طريقة لتحديد ما إذا كان كائن Javascript موجودًا أم لا. فقط إذا كنت واضحًا جدًا بشأن تفاصيل تنفيذ لغة Javascript، يمكنك معرفة الفرق بينهما.
الطريقة الأولى للكتابة
حدسيًا، قد تعتقد أنه يمكنك الكتابة:
انسخ رمز الكود كما يلي:
إذا (!myObj) {
myObj = { };
}
ومع ذلك، عند تشغيل هذا الرمز، سيقوم المتصفح مباشرة بإلقاء خطأ ReferenceError، مما يتسبب في مقاطعة العملية. ما هو الخطأ؟
بالمناسبة، عندما تحدد عبارة if ما إذا كان myObj فارغًا، فإن المتغير غير موجود بعد، لذلك يتم الإبلاغ عن خطأ. قم بتغييره إلى ما يلي وسيتم تشغيله بشكل صحيح.
انسخ رمز الكود كما يلي:
إذا (!myObj) {
var myObj = { };
}
لماذا لا يوجد خطأ بعد إضافة فار؟ هل يمكن في هذه الحالة، عندما تصدر عبارة if حكمًا، هل myObj موجود بالفعل؟
للإجابة على هذا السؤال، يجب أن تعرف كيف يعمل مترجم جافا سكريبت. لغة جافا سكريبت هي "التحليل أولاً، والتشغيل لاحقًا". وقد تم بالفعل إكمال تعريف المتغير أثناء التحليل، لذا فإن الكود أعلاه يعادل في الواقع:
انسخ رمز الكود كما يلي:
فار myObj;
إذا (!myObj) {
var myObj = { };
}
لذلك، عندما تصدر عبارة if حكمًا، يكون myObj موجودًا بالفعل، لذلك لا يتم الإبلاغ عن أي خطأ. هذا هو تأثير "الرفع" للأمر var. يقوم مترجم Javascript فقط "بترويج" المتغيرات المحددة بواسطة الأمر var، ولا يعمل على المتغيرات التي تم تعيينها مباشرة دون استخدام الأمر var، ولهذا السبب سيتم الإبلاغ عن خطأ إذا لم تتم إضافة var.
الطريقة الثانية للكتابة
بالإضافة إلى الأمر var، هناك إعادة كتابة أخرى يمكنها أيضًا الحصول على النتائج الصحيحة:
انسخ رمز الكود كما يلي:
إذا (!window.myObj) {
myObj = { };
}
النافذة هي كائن المستوى الأعلى في JavaScript، وجميع المتغيرات العامة هي خصائصه. لذلك، فإن تحديد ما إذا كان myobj فارغًا يعادل تحديد ما إذا كان كائن النافذة يحتوي على سمة myobj، بحيث يمكن تجنب أخطاء ReferenceError التي تحدث بسبب عدم تعريف myObj. ومع ذلك، من توحيد الكود، من الأفضل إضافة var إلى السطر الثاني:
انسخ رمز الكود كما يلي:
إذا (!window.myObj) {
var myObj = { };
}
أو تكتب هكذا:
إذا (!window.myObj) {
window.myObj = { };
}
الطريقة الثالثة للكتابة
عيب طريقة الكتابة المذكورة أعلاه هو أنه في بعض بيئات التشغيل (مثل V8 وRhino)، قد لا تكون النافذة كائنًا عالي المستوى. لذا فكر في إعادة كتابتها على النحو التالي:
انسخ رمز الكود كما يلي:
إذا (!this.myObj) {
this.myObj = { };
}
على مستوى المتغيرات العامة، تشير الكلمة الأساسية this دائمًا إلى متغير المستوى الأعلى، بحيث يمكن أن تكون مستقلة عن بيئات التشغيل المختلفة.
الطريقة الرابعة للكتابة
ومع ذلك، فإن طريقة الكتابة المذكورة أعلاه أقل قابلية للقراءة، ومؤشر ذلك متغير وعرضة للخطأ، لذلك نعيد كتابتها بشكل أكبر:
انسخ رمز الكود كما يلي:
فار عالمي = هذا؛
إذا (!global.myObj) {
global.myObj = { };
}
من الواضح جدًا استخدام المتغير المخصص global لتمثيل كائن المستوى الأعلى.
الطريقة الخامسة للكتابة
يمكنك أيضًا استخدام عامل التشغيل typeof لتحديد ما إذا كان myObj محددًا أم لا.
انسخ رمز الكود كما يلي:
إذا (نوع myObj == "غير محدد") {
var myObj = { };
}
هذه هي الطريقة الأكثر استخدامًا حاليًا لتحديد ما إذا كان كائن JavaScript موجودًا أم لا.
الطريقة السادسة للكتابة
نظرًا لأن قيمة myObj تساوي بشكل مباشر قيمة undef عندما يتم تعريفها ولكن لم يتم تعيينها، فيمكن تبسيط طريقة الكتابة أعلاه:
انسخ رمز الكود كما يلي:
إذا (myObj == غير محدد) {
var myObj = { };
}
هناك شيئان يجب ملاحظتهما هنا: أولاً، لا يمكن أن تكون الكلمة الأساسية var في السطر الثاني مفقودة، وإلا سيحدث خطأ مرجعي. ثانيًا، لا يمكن إضافة غير محدد بعلامات اقتباس مفردة أو علامات اقتباس مزدوجة، لأنه تتم مقارنة نوع البيانات غير المحدد هنا. ليست "غير محددة" هذه السلسلة.
الطريقة السابعة للكتابة
لا تزال طريقة الكتابة المذكورة أعلاه صحيحة في حالة "المقارنة الدقيقة" (===):
انسخ رمز الكود كما يلي:
إذا (myObj === غير محدد) {
var myObj = { };
}
الطريقة الثامنة للكتابة
وفقًا لتصميم لغة JavaScript، غير محدد == null، لذا فإن مقارنة ما إذا كان myObj يساوي null يمكن أيضًا الحصول على النتيجة الصحيحة:
انسخ رمز الكود كما يلي:
إذا (myObj == فارغة) {
var myObj = { };
}
ومع ذلك، على الرغم من صحة نتائج التشغيل، من وجهة نظر دلالية، فإن طريقة الحكم هذه خاطئة ويجب تجنبها. نظرًا لأن القيمة null تشير إلى كائن فارغ تم تعيين قيمة خالية له، أي أن هذا الكائن له قيمة بالفعل، بينما يشير غير محدد إلى كائن غير موجود أو ليس له قيمة معينة. لذلك، يمكن استخدام "عامل المقارنة" (==) فقط هنا. إذا تم استخدام "عامل المقارنة الدقيق" (===) هنا، فسيحدث خطأ.
الطريقة التاسعة للكتابة
يمكنك أيضًا استخدام عامل التشغيل in لتحديد ما إذا كان myObj هو سمة لكائن المستوى الأعلى:
انسخ رمز الكود كما يلي:
إذا (!('myObj' في النافذة)) {
window.myObj = { };
}
الطريقة العاشرة للكتابة
أخيرًا، استخدم التابع hasOwnProperty لتحديد ما إذا كان myObj خاصية لكائن المستوى الأعلى:
انسخ رمز الكود كما يلي:
إذا (!this.hasOwnProperty('myObj')) {
this.myObj = { };
}
تلخيص
1. إذا حددت فقط ما إذا كان الكائن موجودًا، فمن المستحسن استخدام طريقة الكتابة الخامسة.
2. إذا كنت بحاجة أيضًا إلى تحديد ما إذا كان الكائن يحتوي على قيمة فارغة، بالإضافة إلى ما إذا كان الكائن موجودًا، فمن المستحسن استخدام الطريقة الأولى للكتابة.
3. ما لم تكن هناك ظروف خاصة، يجب الإعلان عن جميع المتغيرات باستخدام الأمر var.
4. لكي تكون عبر الأنظمة الأساسية، يوصى بتجنب استخدام النافذة لتمثيل كائنات المستوى الأعلى.
5. في لغة جافا سكريبت، من السهل الخلط بين القيمة الفارغة وغير المحددة. في الحالات التي قد يكون فيها كلاهما متورطًا، يوصى باستخدام عامل التشغيل "المقارنة الدقيقة" (===).