لن أعرف هذا أيضًا ، اذهب إلى المنزل والمزرعة.
نسخة الكود كما يلي:
حذف thisisoBject [المفتاح]
أو
حذف thisisoBject.Key
بالمناسبة ، دعنا نتحدث عن استخدام الحذف
قبل بضعة أسابيع ، أتيحت لي الفرصة لقراءة كتاب JavaScript الموجهة إلى Stoyan Stoyan Stefanov. تم تصنيف هذا الكتاب بشكل كبير على Amazon (12 مراجعة ، 5 نجوم) ، لذلك كنت فضوليًا لمعرفة ما إذا كان هذا الكتاب الموصى به ، لذلك بدأت في قراءة فصل الوظائف. إنني أقدر حقًا الطريقة التي يشرح بها هذا الكتاب الأشياء ، والأمثلة منظمة بطريقة جميلة للغاية وتدريجية ، ويبدو أنه حتى المبتدئين يمكنهم بسهولة إتقان هذه المعرفة. ومع ذلك ، على الفور تقريبًا ، اكتشفت سوء فهم مثير للاهتمام خلال الفصل - حذف الوظائف الوظيفية. هناك أيضًا بعض الأخطاء الأخرى (مثل الفرق بين إعلانات الوظيفة وتعبيرات الوظائف) ، لكننا لن نناقشها في هذا الوقت.
يدعي الكتاب:
"يتم التعامل مع الوظيفة كمتغير طبيعي - يمكن نسخها في متغيرات مختلفة ، أو حتى حذفها". مثال مرتبط بهذا التفسير:
نسخة الكود كما يلي:
var sum = function (a ، b) {return a + b ؛}
var add = sum ؛
حذف مجموع
حقيقي
نوع typeof ؛
"غير محدد"
تجاهل بعض المنقصات المفقودة ، هل يمكنك معرفة أين توجد الأخطاء في هذه الرموز؟ من الواضح أن الخطأ هو أن تشغيل متغير SUM لن ينجح. يجب ألا يعود تعبير الحذف بشكل صحيح ، ويجب ألا يعود Typeof Sum "غير محدد". كل هذا لأنه من المستحيل حذف المتغيرات في جافا سكريبت. على الأقل ، من المستحيل في طريقة الإعلان هذه.
إذن ، ماذا حدث بالضبط في هذا المثال؟ هل هو خطأ؟ أو استخدام خاص؟ ربما لا. هذا الرمز هو في الواقع الإخراج الحقيقي في وحدة التحكم Firebug ، ويجب أن يكون Stoyan قد استخدمه كأداة للاختبار السريع. يبدو الأمر وكأن Firebug يتبع بعض قواعد الحذف الأخرى. إنه Firebug الذي تسبب في ضلال ستويان! إذن ، ماذا حدث بالضبط هنا؟
قبل الإجابة على هذا السؤال ، نحتاج أولاً إلى فهم كيفية عمل مشغل الحذف في JavaScript: ما الذي يمكن حذفه بالضبط وما الذي لا يمكن حذفه؟ اليوم ، سأحاول شرح هذا بالتفصيل. سننظر إلى سلوك Firebug "الغريب" وندرك أنه ليس غريبًا. سنقوم بتعمق في ما هو مخفي وراء الكواليس حيث المتغيرات والوظائف ، وتعيين القيم للسمات وحذفها. سننظر في توافق المتصفح وبعض الأخطاء الأكثر شهرة. سنناقش أيضًا النمط الصارم لـ ES5 ، وكيف يغير سلوك مشغلي الحذف.
سأقوم بتبديل JavaScript و Ecmascript ، وكلاهما يعني ecmascript (ما لم يكن من الواضح أن تطبيق Mozilla JavaScript)
كما هو متوقع ، على الإنترنت ، فإن تفسيرات الحذف نادرة للغاية. ربما تكون مقالة MDC هي أفضل مورد لفهمها ، ولكنها للأسف ، تفتقر إلى بعض التفاصيل المثيرة للاهتمام حول الموضوع. الغريب أن أحد الأشياء المنسية هو سبب المظهر الغريب لـ Firebug. ومرجع MSDN لا جدوى تقريبًا في هذه الجوانب.
نظرية
لذا ، لماذا يمكننا حذف خصائص كائن:
نسخة الكود كما يلي:
var o = {x: 1} ؛
حذف الثور. // حقيقي
ثور؛ // غير محدد
لكن الكائن أعلن مثل هذا لا يمكن حذفه:
نسخة الكود كما يلي:
var x = 1 ؛
حذف x ؛ // خطأ شنيع
x ؛ // 1
أو الوظيفة:
نسخة الكود كما يلي:
الدالة x () {}
حذف x ؛ // خطأ شنيع
نوع X ؛ // "وظيفة"
ملاحظة: عندما لا يمكن حذف خاصية ، سيعود مشغل الحذف فقط.
لفهم ذلك ، نحتاج أولاً إلى إتقان هذه المفاهيم حول الحالات المتغيرة وخصائص السمة - نادراً ما يتم ذكر هذه المفاهيم في كتب JavaScript. سأحاول مراجعة هذه المفاهيم بإيجاز في الفقرات القليلة القادمة. من الصعب فهم هذه المفاهيم! إذا كنت لا تهتم "لماذا تعمل هذه الأشياء بهذه الطريقة" ، فما عليك سوى تخطي هذا الفصل.
نوع الكود:
في ECMASCRIPT ، هناك 3 أنواع مختلفة من التعليمات البرمجية القابلة للتنفيذ: الرمز العالمي ، رمز الوظيفة ورمز EVAL. هذه الأنواع هي أكثر أو أقل توضيحية ذاتية من حيث الاسم ، إليك نظرة عامة موجزة:
عندما يتم اعتبار جزء من رمز المصدر برنامجًا ، سيتم تنفيذه في بيئة عالمية ويعتبر رمزًا عالميًا. في بيئة المتصفح ، عادة ما يتم تفسير محتويات عناصر البرنامج النصي على أنها برامج وبالتالي يتم تنفيذها كرمز عالمي.
من الواضح أن أي رمز يتم تنفيذه مباشرة في وظيفة يعتبر رمز الوظيفة. في المتصفح ، عادة ما يتم تفسير محتوى خصائص الأحداث (مثل <p onClick = "...">) على أنها رمز وظيفة.
أخيرًا ، يتم تفسير نص الكود المطبق على الوظيفة المدمجة على أنه رمز تقييم. قريباً سنكتشف سبب كون هذا النوع مميزًا.
سياق التنفيذ:
عند تنفيذ رمز ECMASCRIPT ، يحدث عادة في سياق تنفيذ محدد. سياق التنفيذ هو مفهوم كيان مجردة إلى حد ما ، والذي يمكن أن يساعد في فهم كيفية عمل النطاق والمتغير. لكل من الرموز الثلاثة القابلة للتنفيذ ، هناك سياق تنفيذ يتوافق معها. عند تنفيذ وظيفة ما ، نقول "التحكم في البرنامج يدخل سياق تنفيذ رمز الوظيفة" ؛ عند تنفيذ جزء من الكود العالمي ، يدخل التحكم في البرنامج سياق تنفيذ الكود العالمي ، إلخ.
كما ترون ، يمكن لسياق التنفيذ أن يشكل مكدسًا منطقيًا. أولاً ، قد يكون هناك جزء من التعليمات البرمجية العالمية وسياق التنفيذ الخاص به ، ثم قد يستدعي هذا الكود وظيفة مع سياق تنفيذ (وظيفة). يمكن أن تسمي هذه الوظيفة وظيفة أخرى ، وما إلى ذلك. حتى لو تم استدعاء الوظيفة بشكل متكرر ، فسيتم إدخالها في سياق تنفيذ جديد في كل مرة يتم استدعاؤها.
كائن نشط (كائن تنشيط) / كائن متغير:
يحتوي كل سياق تنفيذ على ما يسمى كائن متغير مرتبط به. على غرار سياق التنفيذ ، فإن الكائن المتغير هو كيان مجردة ، وهي آلية تستخدم لوصف مثيلات متغيرة. ومن المثير للاهتمام ، عادة ما تتم إضافة المتغيرات والوظائف المعلنة في الكود المصدر إلى هذا الكائن المتغير كخصائص.
عندما يدخل التحكم في البرنامج سياق تنفيذ الكود العالمي ، يتم استخدام كائن عالمي ككائن متغير. هذا هو بالضبط السبب في أن متغيرات الوظائف المعلنة تصبح خصائص كائن عالمي.
نسخة الكود كما يلي:
/ * تذكر أن "هذا" يشير إلى الكائن العالمي عندما يكون في النطاق العالمي */
var global_object = this ؛
var foo = 1 ؛
global_object.foo ؛ // 1
foo === global_object.foo ؛ // حقيقي
شريط الدالة () {}
typeof global_object.bar ؛ // "وظيفة"
global_object.bar === BAR ؛ // حقيقي
حسنًا ، ستصبح المتغيرات العالمية خصائص للكائنات العالمية ، ولكن ماذا يحدث للمتغيرات المحلية (تلك المحددة في رمز الوظيفة)؟ في الواقع ، يتصرفون بشكل مشابه جدًا: سيصبحون خصائص كائنات متغيرة (كائنات متغيرة). الفرق الوحيد هو أنه عندما يكون كائن الوظيفة ، فإن الكائن المتغير ليس كائنًا عالميًا ، بل هو كائن تنشيط يسمى. يتم إنشاء الكائن النشط في كل مرة يدخل فيها سياق تنفيذ رمز الوظيفة.
ليس فقط المتغيرات والوظائف المعلنة في رمز الوظيفة ستصبح خصائص للكائن النشط ؛ سيحدث هذا أيضًا على كل معلمة دالة (الاسم المقابل للمعلمة الرسمية المقابلة) وكائن وسيطات خاص (اسم الوسائط). لاحظ أن الكائن النشط هو آلية وصف داخلي ولا يمكن الوصول إليها في رمز البرنامج.
نسخة الكود كما يلي:
(وظيفة (فو) {
var bar = 2 ؛
وظيفة baz () {}
/*
من الناحية التجريدية ،
يصبح كائن "وسيطات" خاص خاصية تحتوي على كائن تنشيط الوظيفة:
Activation_object.arguments ؛ // كائن الوسيطات
... وكذلك الحجة "فو":
Activation_object.foo ؛ // 1
... وكذلك المتغير "شريط":
Activation_object.bar ؛ // 2
... وكذلك الوظيفة المعلنة محليًا:
Typeof Activation_object.baz ؛ // "وظيفة"
*/
}) (1) ؛
أخيرًا ، تصبح المتغيرات المعلنة في رمز EVAL خصائص للكائنات المتغيرة في سياق المتصل. يستخدم رمز eval ببساطة كائنات متغيرة في سياق تنفيذ الكود الذي يطلق عليه.
نسخة الكود كما يلي:
var global_object = this ؛
/* `foo` يتم إنشاء خاصية كائن متغير السياق استدعاء ،
وهو في هذه الحالة كائن عالمي */
eval ('var foo = 1 ؛') ؛
global_object.foo ؛ // 1
(وظيفة(){
/* يتم إنشاء BAR "كخاصية كائن متغير السياق استدعاء ،
الذي في هذه الحالة هو كائن تنشيط لاحتواء دالة */
eval ('var bar = 1 ؛') ؛
/*
من الناحية التجريدية ،
Activation_object.bar ؛ // 1
*/
}) () ؛
سمات الممتلكات
نحن هنا تقريبا. الآن بعد أن أدركنا جيدًا ما يحدث مع المتغيرات (تصبح خصائص) ، فإن المفهوم الوحيد المتبقي المتبقي هو سمات الملكية. يمكن أن تحتوي كل سمة على 0 أو أكثر من الخصائص ، والتي يتم اختيارها من المجموعات التالية: readonly ، dontenum ، dontdelete والداخلية. يمكنك التفكير فيها كعلامات - ميزة يمكن أن توجد أو لا توجد في الخصائص. لمناقشتنا اليوم ، نحن مهتمون فقط بـ Dontdelete.
عندما تصبح المتغيرات والوظائف المعلنة سمات للكائنات المتغيرة (أو كائنات نشطة من رمز الوظيفة ، أو الكائنات العالمية للرمز العالمي) ، يتم إنشاء هذه السمات باستخدام سمة سمة Dontdelete. ومع ذلك ، لن يتم تضمين أي سمات صريحة (أو ضمنية) مع سمة Dontdelete. لهذا السبب يمكننا حذف بعض السمات ولكن لا يمكننا حذف الآخرين.
نسخة الكود كما يلي:
var global_object = this ؛
/* `foo` هي خاصية لكائن عالمي.
يتم إنشاؤه عبر إعلان متغير ، وبالتالي فإن سمة Dontdelete.
هذا هو السبب في أنه لا يمكن حذفه. */
var foo = 1 ؛
حذف فو ؛ // خطأ شنيع
Typeof Foo ؛ // "رقم"
/* `BAR` هو خاصية كائن عالمي.
يتم إنشاؤه عبر إعلان الوظيفة ، وبالتالي فإن سمة Dontdelete.
هذا هو السبب في أنه لا يمكن حذفه أيضًا. */
شريط الدالة () {}
حذف الشريط // خطأ شنيع
شريط نوع // "وظيفة"
/* `baz` هي أيضًا خاصية لكائن عالمي.
ومع ذلك ، يتم إنشاؤه عبر تعيين الممتلكات وبالتالي ليس لديه سمة Dontdelete.
هذا هو السبب في أنه يمكن حذفه. */
global_object.baz = 'blah' ؛
DELETE GLOBAL_OBJECT.BAZ ؛ // حقيقي
typeof global_object.baz ؛ // "غير محدد"
كائنات مدمجة و dontdelete
لذلك ، هذا كله عن ذلك (Dontdelete): خاصية خاصة للعقار يتحكم في ما إذا كان يمكن حذف هذه الخاصية. لاحظ أنه تم تحديد بعض الكائنات المدمجة لاحتواء Dontdelete ، لذلك لا يمكن حذفها. على سبيل المثال ، لم يكن متغير وسيطة خاصة (أو ، كما نعلم الآن ، خاصية كائن نشط). خاصية طول مثيل الوظيفة لديها أيضا خاصية لا dontdelete.
نسخة الكود كما يلي:
(وظيفة(){
/ * لا يمكن حذف "الحجج" ، لأنه لا يظهر *//
حذف الحجج // خطأ شنيع
حجج نوع ؛ // "هدف"
/* لا يمكن حذف الوظيفة "الطول" ؛ كما أنه يحتوي على Dontdelete */
الدالة f () {}
حذف F.length ؛ // خطأ شنيع
Typeof F.length ؛ // "رقم"
}) () ؛
السمة المقابلة لمعلمة الوظيفة تحتوي أيضًا على ميزة Dontdelete منذ تأسيسها ، لذلك لا يمكننا حذفها.
نسخة الكود كما يلي:
(وظيفة (فو ، بار) {
حذف فو ؛ // خطأ شنيع
فو // 1
حذف الشريط // خطأ شنيع
حاجِز؛ // 'بلاه'
}) (1 ، 'بلاه') ؛
المهمة غير المعلنة:
قد تتذكر أيضًا أن المهمة غير المعلنة تنشئ خاصية على الكائن العالمي ما لم يتم العثور على العقار في مكان آخر في سلسلة النطاق هذه قبل الكائن العالمي. والآن نعلم الفرق بين تعيين الممتلكات والإعلان المتغير - هذا الأخير يحدد خاصية Dontdelete ، لكن الأول لا. يجب أن نكون واضحين لماذا تنشئ مهمة غير معلنة خاصية قابلة للحذف.
نسخة الكود كما يلي:
var global_object = this ؛
/* إنشاء خاصية عالمية عبر إعلان متغير ؛ الخاصية لا تتولىل */
var foo = 1 ؛
/* إنشاء خاصية عالمية عبر مهمة غير معلنة ؛ الخاصية لا تحتوي على dontdelete */
شريط = 2 ؛
حذف فو ؛ // خطأ شنيع
Typeof Foo ؛ // "رقم"
حذف الشريط // حقيقي
شريط نوع // "غير محدد"
يرجى ملاحظة: يتم تحديد الخصائص عند إنشاء السمة ، ولا تعدل المهام اللاحقة خصائص السمات الحالية. من المهم جدًا فهم هذا الاختلاف.
نسخة الكود كما يلي:
/ * `foo` يتم إنشاء خاصية مع dontdelete */
وظيفة foo () {}
/* لا تعدل المهام اللاحقة السمات. Dontdelete لا يزال هناك! */
foo = 1 ؛
حذف فو ؛ // خطأ شنيع
Typeof Foo ؛ // "رقم"
/* ولكن تعيين خاصية غير موجودة ،
ينشئ تلك الخاصية ذات سمات فارغة (وهكذا بدون Dontdelete) */
this.bar = 1 ؛
حذف الشريط // حقيقي
شريط نوع // "غير محدد"
ارتباك Firebug:
ماذا يحدث في Firebug؟ لماذا يمكن حذف المتغيرات المعلنة في وحدة التحكم؟ أليس هذا مخالفًا لما تعلمناه من قبل؟ حسنًا ، كما قلت من قبل ، فإن رمز EVAL له أداء خاص عند مواجهة إعلانات متغيرة. يتم إنشاء المتغيرات المعلنة في EVAL كسمات بدون سمة Dontdelete.
نسخة الكود كما يلي:
eval ('var foo = 1 ؛') ؛
فو // 1
حذف فو ؛ // حقيقي
Typeof Foo ؛ // "غير محدد"
وبالمثل ، بالمثل ، عند استدعاء رمز الوظيفة:
نسخة الكود كما يلي:
(وظيفة(){
eval ('var foo = 1 ؛') ؛
فو // 1
حذف فو ؛ // حقيقي
Typeof Foo ؛ // "غير محدد"
}) () ؛
هذا هو الأساس لسلوك Firebug غير طبيعي. سيتم تحليل جميع النصوص في وحدة التحكم وتنفيذها كرمز EVER ، بدلاً من رمز عالمي أو وظائف. من الواضح أن جميع المتغيرات المعلنة هنا ستصبح في نهاية المطاف خصائص بدون سمة Dontdelete ، بحيث يمكن حذفها بسهولة. نحتاج إلى فهم الفرق بين الكود العالمي ووحدة التحكم في Firebug.
حذف المتغيرات من خلال eval:
يمكن أن يسمح لنا سلوك Eval المثير للاهتمام ، إلى جانب جانب آخر من ECMASCRIPT ، من الناحية الفنية بحذف خصائص "غير قابلة للحملية". شيء واحد حول إعلانات الوظيفة هو أنها قادرة على تجاوز المتغيرات بنفس الاسم في نفس سياق التنفيذ.
نسخة الكود كما يلي:
الدالة x () {}
var x ؛
نوع X ؛ // "وظيفة"
لاحظ كيف تحصل إعلانات الوظيفة على متغيرات الأولوية والكتابة مع نفس الاسم (أو ، وبعبارة أخرى ، نفس الخصائص في الكائن المتغير). وذلك لأن تصريحات الوظيفة يتم إنشاء مثيل لها بعد الإعلان المتغير ويُسمح لهم بالكتابة فوقها (إعلانات متغيرة). لا تحل إعلانات الوظيفة محل قيمة الخاصية فحسب ، بل تحل أيضًا محل خصائص تلك الخاصية. إذا أعلننا وظيفة من خلال eval ، فيجب أن تحل هذه الوظيفة محل خصائص الخاصية الأصلية (التي تم استبدالها) بخصائصها الخاصة. ونظرًا لأن المتغيرات التي تم إعلانها من خلال eval إنشاء خصائص بدون سمة dontdelete ، فإن إنشاء هذه الوظيفة الجديدة سيؤدي فعليًا إلى إزالة السمة الحالية من الخاصية من الخاصية ، بحيث يمكن حذف خاصية (ومن الواضح أن قيمتها إلى الوظيفة التي تم إنشاؤها حديثًا).
نسخة الكود كما يلي:
var x = 1 ؛
/ * لا يمكن حذف ، `x` لديه dontdelete */
حذف x ؛ // خطأ شنيع
نوع X ؛ // "رقم"
eval ('function x () {}') ؛
خاصية Property/`X` الآن وظيفة المراجع ، ويجب ألا يكون لها عدم وجود dontelete */
نوع X ؛ // "وظيفة"
حذف x ؛ // يجب أن يكون "صحيحًا"
نوع X ؛ // يجب أن يكون "غير محدد"
لسوء الحظ ، هذا "الخداع" لا يعمل في أي تطبيق في الوقت الحاضر. ربما أفتقد شيئًا هنا ، أو ربما يكون السلوك غامضًا جدًا لأن المنفذ لا يلاحظ ذلك.
توافق المتصفح:
من المفيد من الناحية النظرية فهم كيفية عمل الأشياء ، ولكن الممارسة هي أهم شيء. هل يتبع المتصفح المعايير عندما يتعلق الأمر بإنشاء/حذف المتغيرات/الخصائص؟ الجواب هو: في معظم الحالات ، نعم.
لقد كتبت مجموعة اختبار بسيطة لاختبار توافق المتصفح مع عوامل الحذف ، بما في ذلك الاختبارات ضمن الكود العالمي ورمز الوظيفة ورمز Eval. تحقق مجموعة الاختبار ما إذا كانت قيمة الإرجاع وقيم السمة الخاصة بمشغل الحذف (كما ينبغي أن تتصرف) يتم حذفها بالفعل. قيمة الإرجاع للحذف ليست بنفس أهمية النتيجة الحقيقية. إذا كان Delete يرجع صحيحًا بدلاً من خطأ ، فهذا ليس مهمًا ، والمهم هو أن السمات مع سمة Dontdelete لا يتم حذفها ، والعكس صحيح.
المتصفحات الحديثة متوافقة بشكل عام. بصرف النظر عن ميزات eval التي ذكرتها سابقًا ، مرت المتصفحات التالية جميع مجموعات الاختبارات: Opera 7.54+ ، Firefox 1.0+ ، Safari 3.1.2+ ، Chrome 4+.
يواجه Safari 2.x و 3.0.4 مشاكل عند حذف معلمات الوظيفة ؛ يبدو أن هذه الخصائص قد تم إنشاؤها دون عدم حذفها ، بحيث يمكن حذفها. يواجه Safari 2.x المزيد من المشكلات - حذف المتغيرات غير المرجعية (مثل Delete 1) سوف يلقي استثناءات ؛ تصريحات الوظيفة تخلق خصائص قابلة للحذف (ولكن ، من الغريب ، لا توجد إعلانات متغيرة) ؛ ستصبح التصريحات المتغيرة في EVAL غير حضرية (ولكن إعلانات الوظيفة قابلة للحذف).
على غرار Safari ، يرمي Konqueror (3.5 ، وليس 4.3) استثناء عند حذف نوع غير مرجع (مثل: حذف 1) ، ويجعل متغيرات الوظيفة غير قابلة للحذف بشكل غير صحيح.
ملاحظة المترجم:
لقد اختبرت أحدث إصدارات Chrome و Firefox و IE ، وحفظت بشكل أساسي الوضع حيث ستفشل جميع التمريرات الأخرى باستثناء 23 و 24. في الوقت نفسه ، اختبرت UC وبعض المتصفحات المحمولة. باستثناء متصفح Nokia E72 المدمج ، الذي فشل أيضًا في 15 و 16 ، تكون المتصفحات المدمجة الأخرى في الغالب هي نفس متصفحات سطح المكتب. ولكن تجدر الإشارة إلى أن المتصفح المدمج في BlackBerry Curve 8310/8900 يمكن أن يمر 23 ، وهو ما فاجأني.
Gecko Dontdelete Bug:
Gecko 1.8.x Browser - Firefox 2.x ، Camino 1.x ، Seamonkey 1.x ، إلخ - يعرض خللًا مثيرًا للاهتمام للغاية ، وسوف تزيل تعيين خاصية صريحة من خاصية Dontdelete ، حتى إذا تم إنشاء هذه الخاصية من خلال إعلانات متغيرة أو إعلانات الوظيفة.
نسخة الكود كما يلي:
وظيفة foo () {}
حذف فو ؛ // خطأ (كما هو متوقع)
Typeof Foo ؛ // "وظيفة" (كما هو متوقع)
/ * الآن تعيين خاصية بشكل صريح */
this.foo = 1 ؛ // يمسح بشكل خاطئ سمة Dontedelete
حذف فو ؛ // حقيقي
Typeof Foo ؛ // "غير محدد"
/ * لاحظ أن هذا لا يحدث عند تعيين خاصية ضمنيًا */
شريط الدالة () {}
شريط = 1 ؛
حذف الشريط // خطأ شنيع
شريط نوع // "الرقم" (على الرغم من أن المخصصات التي تم استبدالها)
من المثير للدهشة أن Internet Explorer 5.5 - 8 اجتاز مجموعة الاختبار الكاملة ، باستثناء أن حذف الأنواع غير المرجعية (مثل Delete 1) سوف يرمي استثناءات (تمامًا مثل Safari القديم). ومع ذلك ، هناك أخطاء أكثر خطورة تحت IE ، وهو أمر غير واضح. ترتبط هذه الأخطاء بالكائن العالمي.
أي الحشرات:
يتحدث هذا الفصل بأكمله عن أخطاء Internet Explorer؟ رائع! إنه لأمر مدهش!
في IE (على الأقل IE 6-8) ، يلقي التعبير التالي استثناء (عند تنفيذه في الكود العالمي):
this.x = 1 ؛
حذف x ؛ // typeerror: الكائن لا يدعم هذا الإجراء
هذا واحد أيضًا ، ولكنه سوف يرمي استثناءات مختلفة ، مما يجعل الأمور أكثر إثارة للاهتمام:
var x = 1 ؛
حذف this.x ؛ // typeerror: لا يمكن حذف "this.x"
هذا يبدو في IE ، لا تنشئ الإعلانات المتغيرة في الكود العالمي سمات على الكائن العالمي. إنشاء سمات حسب المهمة (this.x = 1) ثم حذفها عن طريق حذف x بعد ذلك يلقي خطأ. إنشاء سمات عن طريق الإعلان (var x = 1) وحذفه بعد ذلك يرمي خطأ آخر.
لكن هذا ليس كل شيء. سيؤدي إنشاء خصائص عن طريق الواجبات الصريحة دائمًا إلى التسبب دائمًا في إلقاء الاستثناءات عند حذفها. لا توجد أخطاء هنا فحسب ، بل يبدو أن الخصائص التي تم إنشاؤها لديها سمة Dontdelete ، والتي لا ينبغي بالطبع.
this.x = 1 ؛
حذف this.x ؛ // typeerror: الكائن لا يدعم هذا الإجراء
نوع X ؛ // "الرقم" (لا يزال موجودًا ، لم يتم حذفه كما كان ينبغي أن يكون!)
حذف x ؛ // typeerror: الكائن لا يدعم هذا الإجراء
نوع X ؛ // "الرقم" (لم يتم حذفه مرة أخرى)
الآن ، نعتقد أنه في ظل IE ، تقوم المهام غير المعلنة (يجب إنشاء الخصائص على الكائنات العالمية) بإنشاء خصائص قابلة للحذف.
x = 1 ؛
حذف x ؛ // حقيقي
نوع X ؛ // "غير محدد"
ومع ذلك ، إذا قمت بحذف هذه الخاصية من خلال هذا المرجع في الكود العالمي (حذف this.x) ، فسيظهر خطأ مماثل.
x = 1 ؛
حذف this.x ؛ // typeerror: لا يمكن حذف "this.x"
إذا أردنا تلخيص هذا السلوك ، فيبدو أن استخدام حذف this.x لحذف المتغيرات من الكود العالمي لن ينجح أبدًا. عندما يتم إنشاء الخاصية في السؤال عن طريق مهمة صريحة (this.x = 1) ، يلقي حذف خطأ ؛ عند إنشاء الخاصية بواسطة مهمة غير معلنة (x = 1) أو عن طريق إعلان (var x = 1) ، حذف يلقي خطأ آخر.
حذف x ، من ناحية أخرى ، يجب إلقاء خطأ فقط عند إنشاء الخاصية عن طريق مهمة صريحة - this.x = 1. إذا تم إنشاء خاصية عن طريق مهمة غير معلمة (x = 1) ، فإن عملية الحذف تعمل كما هو متوقع.
فكرت في هذه القضية مرة أخرى في سبتمبر. اقترح غاريت سميث أنه تحت IE ،
"يتم تطبيق الكائن المتغير العالمي ككائن JScript ، ويتم تنفيذ الكائن العالمي بواسطة المضيف."
استخدم غاريت إدخال مدونة إريك ليبرت كمرجع.
يمكننا أن نؤكد هذه النظرية أكثر أو أقل من خلال تنفيذ بعض الاختبارات. لاحظ أن هذه النافذة تشير إلى نفس الكائن (إذا استطعنا الوثوق بمشغل ===) ، لكن الكائن المتغير (الكائن الذي يوجد فيه إعلان الوظيفة) يختلف عن ما يشير إليه هذا.
نسخة الكود كما يلي:
/ * في الكود العالمي */
وظيفة getBase () {return this ؛ }
getBase () === this.getBase () ؛ // خطأ شنيع
this.getBase () === this.getBase () ؛ // حقيقي
window.getBase () === this.getBase () ؛ // حقيقي
window.getBase () === getBase () ؛ // خطأ شنيع
سوء الفهم:
إن جمال فهم سبب عمل الأشياء بهذه الطريقة لا يجب التقليل من شأنه. لقد رأيت بعض سوء الفهم حول مشغل الحذف على الإنترنت. على سبيل المثال ، تشرح الإجابة على stackoverflow (مع تصنيف مرتفع بشكل مدهش)
"عندما لا يكون المعامل المستهدف خاصية كائن ، يجب أن يكون حذف التشغيل."
الآن بعد أن فهمنا جوهر سلوك عملية الحذف ، يصبح الخطأ في هذه الإجابة واضحًا. لا يميز الحذف بين المتغيرات والسمات (في الواقع ، بالنسبة للحذف ، فهي كلاهما نوعان مرجعيين) وفي الواقع يهتم فقط بالسمات غير المنهلة (وما إذا كانت السمات نفسها موجودة).
من المثير للاهتمام أيضًا رؤية مختلف سوء الفهم تدحض بعضها البعض. في نفس الموضوع ، اقترح شخص واحد أولاً أن يحذف المتغيرات فقط (لن يعمل هذا فقط ما لم يتم إعلانه في EVAL) ، في حين قدم شخص آخر تصحيح الخلل لكيفية استخدام الحذف لحذف المتغيرات في الكود العالمي ، ولكن ليس في رمز الوظيفة.
كن حذرًا جدًا بشأن شرح JavaScript على الإنترنت. الطريقة المثالية هي أن تفهم دائمًا جوهر المشكلة. ؛)
حذف واستضافة الكائن (كائن المضيف):
خوارزمية الحذف مثل هذا:
إرجاع صحيح إذا لم يكن المعامل نوع مرجعي
إذا لم يكن للكائن سمة مباشرة لهذا الاسم ، فالتراجع عن ذلك (كما نعلم ، يمكن أن يكون الكائن كائنًا نشطًا أو كائنًا عالميًا)
إذا كانت العقار موجودًا ولكن لديه سمة Dontdelete ، فأرجع كاذبة
في حالات أخرى ، احذف السمة وإرجاع صحيح
ومع ذلك ، لا يمكن التنبؤ بسلوك مشغل الحذف على الكائن المضيف. وهذا السلوك ليس خاطئًا في الواقع: (وفقًا للمعيار) ، يُسمح للكائن المضيف بتنفيذ أي سلوك لعدة مشغلات مثل طريقة القراءة ([[GET]]) ، والكتابة (طريقة [[PUT]] الداخلية) وحذف (طريقة [[DELETE]]). هذا النعمة للسلوك المخصص [[delete]] هو ما يجعل الكائن المضيف مربكًا.
لقد رأينا بعض المراوغات IE ، حيث حذف كائنات محددة (والتي يتم تنفيذها بوضوح ككائنات مضيفة) سوف يرمي أخطاء. سوف ترمي بعض إصدارات Firefox عند حذف window.location. عندما تكون المعاملات كائنات مضيفة ، لا يمكنك الوثوق بقيمة إرجاع الحذف. دعونا نرى ما يحدث في Firefox:
نسخة الكود كما يلي:
/ * "Alert" هي خاصية مباشرة لـ "نافذة" (إذا كنا نؤمن بـ "hasownproperty`) */
window.hasownproperty ('Alert') ؛ // حقيقي
حذف Window.Alert ؛ // حقيقي
typeof window.alert ؛ // "وظيفة"
حذف Window.Alert إرجاع صحيح ، حتى لو لم يكن هناك سبب لأن هذه الخاصية تسبب مثل هذه النتيجة على الإطلاق. سيتم حلها إلى مرجع (لذلك لن يعود بشكل صحيح في الخطوة الأولى). هذه خاصية مباشرة لكائن نافذة (لذلك لن يعود صحيحًا في الخطوة الثانية). وبالتالي فإن الحالة الوحيدة التي يمكن أن تُعود فيها Delete True هي الوصول إلى الخطوة الرابعة وحذف تلك الخاصية بالفعل. ومع ذلك ، لم يتم حذف هذه الخاصية.
أخلاق هذه القصة هي: لا تثق في الكائن المضيف.
الوضع الصارم ES5:
إذن ، ما الذي يجلبه ECMASCRIPT5 الصارم؟ يقدم بعض القيود. عندما يكون التعبير عن مشغل الحذف مرجعًا مباشرًا إلى متغير أو معلمة دالة أو معرف دالة ، سيتم طرح خطأ في بناء الجملة. بالإضافة إلى ذلك ، إذا كانت الخاصية تحتوي على خاصية داخلية [[تكوين]] == خطأ ، فسيتم طرح خطأ في النوع.
نسخة الكود كما يلي:
(وظيفة (فو) {
"استخدام صارم" ؛ // تمكين الوضع الصارم داخل هذه الوظيفة
فار بار
وظيفة baz () {}
حذف فو ؛ // syntaxerror (عند حذف الوسيطة)
حذف الشريط // syntaxerror (عند حذف المتغير)
حذف الباز ؛ // syntaxerror (عند حذف المتغير الذي تم إنشاؤه مع إعلان الوظيفة)
/ * `length` من مثيلات الوظيفة لديه {[[configable]]: false} */
delete (function () {}). الطول ؛ // typeerror
}) () ؛
بالإضافة إلى ذلك ، فإن حذف المتغيرات غير المعلنة (أو المراجع التي لم يتم حلها) سوف يرمي أيضًا أخطاء بناء الجملة:
"استخدام صارم" ؛
حذف i_dont_exist ؛ // Syntaxerror
تتصرف المهمة غير المعلنة بشكل مشابه للمتغيرات غير المعلنة في وضع صارم (باستثناء هذه المرة ، فإنه يرفع خطأ اقتباس بدلاً من خطأ في بناء الجملة):
"استخدام صارم" ؛
i_dont_exist = 1 ؛ // Referenceerror
كما تفهم الآن ، فإن جميع القيود منطقية إلى حد ما ، لأن حذف المتغيرات ، وإعلانات الوظائف ، والمعلمات يمكن أن تسبب الكثير من الارتباك. بدلاً من تجاهل عملية الحذف بصمت ، يأخذ النمط الصارم إجراءً أكثر راديكالية وصفية.
تلخيص:
انتهى الأمر إلى أن منشور المدونة هذا طويل جدًا ، لذلك لن أتحدث عن شيء مثل استخدام الحذف لحذف كائن صفيف أو ما يعنيه. يمكنك الرجوع إلى التفسير الخاص لمقال MDC (أو قراءة المعايير والقيام بتجاربك الخاصة).
فيما يلي ملخص موجز لكيفية عمل عمليات الحذف في JavaScript:
المتغيرات وإعلانات الوظائف هي خصائص للكائنات النشطة أو الكائنات العالمية
تحتوي السمات على بعض الخصائص ، و dontdelete هي السمة التي تحدد ما إذا كان يمكن حذف هذه السمة.
المتغيرات وإعلانات الوظائف في رمز العالم أو الدالة دائمًا تنشئ سمات مع سمات Dontdelete.
معلمات الوظيفة هي دائمًا سمات للكائن النشط ويرافقها Dontdelete.
المتغيرات والوظائف المعلنة في رمز EVAL تنشئ دائمًا خصائص بدون Dontdelete.
لا تحتوي الخصائص الجديدة على سمات عند إنشائها (بالطبع لا يوجد عدم إلغاء).
يُسمح للكائن المضيف بتحديد كيفية الرد على عملية الحذف.
إذا كنت تريد أن تكون أكثر دراية بما هو موصوف هنا ، راجع مواصفات الإصدار الثالث من ECMA-262.
آمل أن تتمكن من الاستمتاع بهذا المقال وتعلم شيء جديد. أي أسئلة أو اقتراحات أو تصحيحات مرحب بها.