في المقالة السابقة ، قدمنا نظرة عامة على الاستعداد المسبق. قبل كتابة منشور المدونة هذا ، خططنا لكتابة بعض الحالات الكلاسيكية. بالنظر إلى أن هذه الحالات أكثر شمولاً ، لدينا منشور المدونة خطوة بخطوة ، وهو أمر أسهل في التعلم والمتعمق.
تسلسل
ذهب زميل إلى المقابلة وطرح القائم بإجراء المقابلة سؤالاً: هل تكتب إغلاقًا وسأقرأه؟ لذا سرعان ما كتب زميلي الرمز التالي:
نسخة الكود كما يلي:
وظيفة fn () {
تنبيه ("Hello JavaScript Closure !!!)
}
fn () ؛
ثم هز القائم بإجراء المقابلة رأسه وقال: "كيف يمكن أن يطلق عليه هذا الإغلاق؟" في النهاية ، كان لدى الاثنان نزاعًا ، وغادر الزميل بشكل حاسم. ما هو شيء المقابلة؟ (هذه القصة خيالية بحتة ، إذا كان هناك أي تشابه ، فهي مصادفة بحتة)
قد تكون عمليات الإغلاق تقنية "متطورة وصعبة" في عيون كثير من الناس ، ولكن في نظر كثير من الناس ، قد تعتبر إغلاقًا:
مثال 1:
نسخة الكود كما يلي:
وظيفة fn () {
وظيفة الإرجاع () {
تنبيه ("مثال 1") ؛
}
}
fn () () ؛
مثال 1 PS: هذا لا يبدو متقدمًا للغاية ، يبدو أن هذا الشخص ليس جيدًا في ذلك!
مثال 2:
نسخة الكود كما يلي:
؛(وظيفة () {
تنبيه ("مثال 2") ؛
}) () ؛
مثال 2 PS: يبدو هذا أكثر تقدماً من السابقين ، وهناك فاصلة فاصلة قبل القوس الأول. لماذا تضيف فاصلة فاصلة؟ حسنًا ، دعنا نترك هذا السؤال هنا أولاً ، وسنتحدث عنه لاحقًا.
مثال 3:
نسخة الكود كما يلي:
~ وظيفة fn () {
تنبيه ("مثال 3")
} () ؛
مثال 3 PS: هذا هو الأكثر تقدمًا ، إنه أمر رائع للغاية. قرأت أقل ، لا تكذب علي!
سيد LUN ليس جيدًا في القراءة ، ويمكنه فقط كتابة "عمليات الإغلاق" الثلاثة. أعتقد أن المدونين يمكنهم كتابة "إغلاق" أكثر وأفضل ؛ هنا ، يرجى إيقاف الهراء أولاً ، ثم دراسة آلية تشغيل الوظيفة. يبدو أن شخصًا ما يعرف ذلك بالفعل ، يجب أن يكون النطاق. لا أريد حقًا إضافة هذا النطاق إلى العنوان ، لذلك أشعر دائمًا أنه مفيد تقريبًا. هذه الأشياء هي في الأصل معًا ، فلماذا أكررها؟ العادة القديمة ، قم بتحميل الكود أولاً:
نسخة الكود كما يلي:
var n = 10 ؛
وظيفة fn () {
تنبيه (ن) ؛
var n = 9 ؛
تنبيه (ن) ؛
}
fn () ؛
ببساطة ، نرسم الصور (يستخدم Master فقط برنامج الرسم الذي يأتي مع Windows. إذا كان هناك أي صور أفضل ، فيرجى التوصية بها للمدونين) لتحليلها:
تحليل 1
من الشكل ، نرى نطاطين ، أحدهما هو نطاق النافذة (النطاق العالي المستوى) ، والآخر هو نطاق خاص يتكون عندما يتم استدعاء FN ؛ ثم ما هو النطاق؟ النطاق هو في الواقع البيئة لتنفيذ الكود. على سبيل المثال ، بيئة التعلم للطالب هي مدرسة ، وهي تعادل نطاقه. إذا كان الطالب مشاغبًا وغالبًا ما يذهب إلى مقهى الإنترنت للعب الألعاب في الليل ، فهذا يعادل تشكيل بيئة خاصة ، وهي مقهى إنترنت. حسنًا! هذه Lizi هي حتى تبدو مثل سيد نفسه ، ولا يسعها إلا أن تنهد: "إذا كنت لا تعمل بجد عندما تكون شابًا ، فسيتم ركلك عندما تكبر". دعنا نعود إلى الموضوع. في الواقع ، يشير تعريف الوظيفة FN إلى وصف قطعة من الكود (المربع الأحمر في الشكل). عندما يسمى هذا FN (الصندوق الأخضر في الشكل) ، سيتم تشكيل نطاق. بطبيعة الحال ، سيتم أيضًا استخلاص الرمز في هذا النطاق قبل التنفيذ. لن أخبرك أنه سيتم تدمير هذا النطاق بعد الإعدام. يتم استدعاء هذا FN مرة أخرى ، وسوف يشكل أيضًا نطاقًا جديدًا ، ثم يتم استخلاصه مسبقًا قبل التنفيذ ، ثم تنفيذ رمز ، ثم تم تدميره أخيرًا بعد التنفيذ.
فهم الإغلاق
نعلم أنه عندما يتم استدعاء وظيفة ، فإن النطاق الخاص (بيئة التنفيذ) سوف يشكل نطاقًا خاصًا (بيئة التنفيذ) ، وهذا النطاق الخاص هو إغلاق. إذا نظرنا إلى الوراء ، هل لا يزال الإغلاق هو "الراقي الراقي وليس من السهل الحصول عليه"؟ دعونا ننظر إلى الوراء في قصة المقابلة الأولى ، والأمثلة الثلاثة التي كتبتها ، فهي في الواقع إغلاق. أن تكون دقيقة ، هذه الأمثلة الثلاثة هي أشكال شائعة للإغلاق.
سيناريوهات التطبيق
الآن هناك شرط: هناك علامة UL في صفحة HTML ، وهناك 5 علامات LI تحت UL. تحتاج إلى أي نقرة على li ، ويفهرس (يبدأ الفهرس من 0) حيث يتم انتشار LI الذي تم النقر عليه. هيكل HTML كما يلي:
نسخة الكود كما يلي:
<ul id = "ul">
<li> قائمة 1 </li>
<li> قائمة 2 </li>
<li> قائمة 3 </li>
<li> قائمة 4 </li>
<li> قائمة 5 </li>
</ul>
كنت ذكيًا وسرعان ما كتبت الرمز التالي:
نسخة الكود كما يلي:
var lis = document.getElementById ('ul'). getElementSbyTagName ('li') ؛
لـ (var i = 0 ، len = lis.length ؛ i <len ؛ i ++) {
lis [i] .onclick = function () {
تنبيه (ط) ؛
} ؛
}
الاختبار النهائي لمعرفة ما إذا كان يتم تنفيذ هذا المطلب تمامًا:
لقد وجدت أنه بغض النظر عن عدد المرات التي نقرت فيها ، ستظهر هذه النتيجة في النهاية. النتيجة المطلوبة هي: انقر فوق القائمة 1 لتبرز 0 ، انقر فوق القائمة 2 لتبرز 1 ، انقر فوق القائمة 3 لتبرز 2 ... في هذه اللحظة ، أريد فقط استخدام هذه الصورة لوصف مزاجي الحالي:
(كيف يبدو عندما يفشل النموذج الأولي في العمل كمتطلبات التصميم أثناء التوضيح)
ماذا علي أن أفعل؟ لماذا يظهر 5 دائمًا؟ هذا صحيح جدا من الناحية النظرية! دعونا نرسم صورة لتحليلها:
في الواقع ، نحن نعطي كل li onclick ، وهي في الواقع سلسلة وصف وظيفة تحفظها. محتوى هذه السلسلة هو المحتوى في المربع الأحمر للصورة أعلاه. إذا كنت لا تزال لا تصدق ذلك ، فلدي الصورة والحقيقة:
أدخل: LIS [4] عندما ننقر على القائمة الخامسة ، فإنه يعادل في الواقع LIS [4] .onclick (). بعد استدعاء وصف الوظيفة هذا ، نعلم أنه عند تنفيذ الوظيفة ، ستشكل نطاقًا خاصًا. في هذا النطاق الخاص ، سيتم استخلاصه مسبقًا أولاً ، ثم يتم تنفيذ الكود. في هذا الوقت ، سوف نبحث عن أنا. لا يوجد أنا تحت النطاق الخاص الحالي ، ثم أجد أنني تحت نطاق النافذة ، لذلك يظهر 5 منببوبًا في كل مرة تنقر فيها.
من الواضح أن الكود أعلاه لا يمكن أن يفي بهذا المطلب. من غير الصحيح كتابة التعليمات البرمجية الخاصة بنا. دعونا نفكر في أسباب المشكلة؟ في الواقع ، السبب هو أنه في كل مرة نقر فيها ، يتم قراءتها في I Under Window. في هذا الوقت ، تبلغ قيمة هذا أنا بالفعل 5 ، وبالتالي فإن الكود التالي متاح:
الطريقة 1:
نسخة الكود كما يلي:
var lis = document.getElementById ('ul'). getElementSbyTagName ('li') ؛
وظيفة fn (i) {
وظيفة الإرجاع () {
تنبيه (ط) ؛
}
}
لـ (var i = 0 ، len = lis.length ؛ i <len ؛ i ++) {
lis [i] .onclick = fn (i) ؛
}
الطريقة 2:
نسخة الكود كما يلي:
var lis = document.getElementById ('ul'). getElementSbyTagName ('li') ؛
لـ (var i = 0 ، len = lis.length ؛ i <len ؛ i ++) {
؛ (وظيفة (i) {
lis [i] .onclick = function () {
تنبيه (ط) ؛
} ؛
})(أنا)؛
}
الطريقة 3:
نسخة الكود كما يلي:
var lis = document.getElementById ('ul'). getElementSbyTagName ('li') ؛
لـ (var i = 0 ، len = lis.length ؛ i <len ؛ i ++) {
lis [i] .onclick = function fn (i) {
وظيفة الإرجاع () {
تنبيه (ط) ؛
}
}(أنا)؛
}
لقد كتبت ثلاث طرق في نفس واحد ، والأفكار متشابهة ، وهي تخزين هذا المتغير أنا في متغير خاص. هنا سأتحدث فقط عن الطريقة الثانية. بالطبع ، إذا فهمت أحدهم ، فسوف تفهم الباقي. وفقًا للاتفاقية ، دعنا نرسم صورة ونحللها خطوة بخطوة:
لقد وصفت تنفيذ الكود بأكمله بالتفصيل. تجدر الإشارة إلى أن سمة onclick لكل LI يجب أن تشغل نطاق (i) {...}) (i). عندما يتم تنفيذ هذه الوظيفة ، لن يتم تدميرها لأنه يشغلها LI الخارجي (هذا LI تحت نطاق النافذة) ، لذلك لن يتم تدمير هذا النطاق. عند النقر على أي li ، function () {Alert (i) ؛ } سيتم تنفيذها وسيشكل نطاق أيضًا. هذا النطاق ليس لدي. سوف ينتقل إلى (function () {...}) (i) النطاق للعثور على i ، وأخيراً العثور على I في المعلمة الرسمية ، ويتم تمرير قيمة هذه المعلمة الرسمية خلال الحلقة ؛ يستخدم هذا المثال بذكاء عمليات الإغلاق لتخزين القيمة ، وحل المشكلة تمامًا.
PS: لقد قلت للتو (الوظيفة (i) {...}) (i) لماذا أضفت فاصلة فاصلة أمامه منع البيان السابق من نسيان إضافة فاصلة فاصلة ، مما يؤدي إلى ارتكاب أخطاء أثناء التحليل ، وهذا كل شيء. بالطبع ، أحد سيناريوهات التطبيق أعلاه هو مبدأ تنفيذ علامات التبويب. يمكن أن تكون هناك طرق تنفيذ أخرى ، مثل طرق السمة المخصصة وإيجاد الفهارس من خلال علاقات عقدة DOM. يتبنى السيد هذه الطريقة فقط لتعميق فهمه للإغلاق.
لخص
الإغلاق ليست طويلة مثل الأسطوريين. جوهر هو فهم تعريفات الوظائف والمكالمات. سيتم تشكيل نطاق خاص جديد عند استدعاء وظيفة. عندما يشغل النطاق من الخارج ، لن يتم تدمير النطاق. سيد لو يقرأ القليل جدا. إذا كان هناك أي أشياء خاطئة ، يرجى تصحيح لي من قبل المدونين. في الوقت نفسه ، أود أن أشكرك على دعمكم لمقال سيد لوك.