في عملية التعلم الأمامي، سنواجه حتما العديد من المشاكل، لذلك سنتحدث اليوم عن سؤالين من منظور المبتدئ:
ما هو الإغلاق؟
ما هي وظائف الإغلاق؟
في الواقع، عمليات الإغلاق موجودة في كل مكان عندما نتعلم JavaScript، ما عليك سوى أن تكون قادرًا على التعرف عليها وقبولها. عمليات الإغلاق ليست أداة تتطلب تعلم بناء جملة أو نمطًا جديدًا لاستخدامه، فهي نتيجة طبيعية لكتابة التعليمات البرمجية بناءً على النطاق المعجمي. نادرًا ما نحتاج إلى إنشاء عمليات إغلاق عن عمد عند كتابة التعليمات البرمجية.
أعتقد أن العديد من الأصدقاء يتمتمون بالفعل في قلوبهم في هذا الوقت، ما هو هذا النطاق المعجمي، لا داعي للذعر، فقط استمع لي ببطء. بمعنى آخر، يتم تحديد النطاق المعجمي من خلال المكان الذي تضع فيه المتغيرات ونطاقات مستوى الكتلة عند كتابة التعليمات البرمجية الخاصة بك، وبالتالي يظل النطاق دون تغيير عندما يقوم المحلل المعجمي بمعالجة التعليمات البرمجية (في معظم الأوقات). ——"JavaScript الذي لا تعرفه"
لنأخذ مثالًا أولاً:
function test(){ فار آر = [] ل(فار ط=0;أنا<10;i++){ آر[i]=وظيفة(){ console.log(i); } } العودة آر } فار ماي آر = اختبار () // ماي آر [0]() // ماي آر [1] () // ... ل(فار ي = 0; ي < 10; ي++){ ماي آر [ي]() } // لتجنب الملل، يتم استخدام حلقة ثانية هنا لاستدعاء الوظيفة في الحلقة الأولى في وظيفة الاختبار وطباعة عشر
نتائج يجب تحليلها وطباعتها بعشرة أرقام من 0 إلى 9 بالتسلسل، لكن حلقة for لا تستغرق وقتًا للتشغيل (يتم إهمالها بالميكروثانية) عندما يعود اختبار الوظيفة إلى 10 function(){console(i );}، لا يتم تنفيذ الوظيفة في المصفوفة في هذا الوقت عندما يستدعي var myArr = test() وظيفة الاختبار، حيث يتم تجاهل وقت تنفيذ حلقة for، i هو بالفعل 10 في هذا الوقت، فما هو. المطبوعة هي 10 من 10.
أعتقد أن شخصًا ما سيسأل في هذا الوقت، ما علاقة هذا بالإغلاق الذي سنتحدث عنه، لذا إذا قمنا بتعديل هذا الرمز قليلاً وقمنا بتغييره إلى تراكم، فكيف يمكننا تنفيذه؟
أعتقد أنه سيكون هناك شخصيات كبيرة في هذا الوقت، ومن سيقول، أليس هذا بهذه البساطة؟
قم بتغيير تعريف var إلى تعريف Let بحيث تصبح حلقة for الأولى نطاقًا على مستوى الكتلة، ثم يمكن أن تصبح تراكمًا. بالطبع لا توجد مشكلة،
ولكن ما نتحدث عنه اليوم هو كيفية تنفيذ تراكم في ES5. ثم دعونا نلقي نظرة على الكود التالي:
function test(){ فار آر = [] ل(فار ط=0;أنا<10;i++){ (وظيفة (ي) { آر[ي]=وظيفة(){ console.log(j); } })(أنا) } العودة آر } فار ماي آر = اختبار () ل(فار ي = 0; ي < 10; ي++){ ماي آر [ي]() }
سيجد الأصدقاء المهتمون بالتأكيد أن هذا هو تغيير جسم الوظيفة في الحلقة إلى وظيفة ذاتية التنفيذ، ولكن نتيجة الإخراج في هذا الوقت هي إخراج عشرة أرقام من 0 إلى 9 بالتسلسل، وهذا يشمل حزمة الإغلاق، عندما نبدأ في تنفيذ هذا الرمز، سيتم استدعاء الحلقة الثانية عشر مرات. عند تنفيذ كل وظيفة ذاتية التنفيذ، سيتم إنشاء كائن AO للوظيفة ذاتية التنفيذ. يوجد كائن AO في هذه الوظيفة ذاتية التنفيذ اسم السمة هو j. عادةً، بعد تنفيذ وظيفة التنفيذ، يجب تدمير كائن AO الخاص بها، ومع ذلك، عند تنفيذ myarr[j] ()، يكون كائن AO الخاص بـ arr[j] في الجزء العلوي من سلسلة النطاق. لقد بحثت الآن عن اسم السمة j، لكنني لم أجده، لقد بحثت في سلسلة النطاق ووجدته في كائن AO للوظيفة ذاتية التنفيذ، لذلك عندما تنتهي الوظيفة ذاتية التنفيذ، يكون AO لن تتم إعادة تدوير الكائن بواسطة آلية جمع البيانات المهملة، وإلا سيتم الإبلاغ عن خطأ عند تنفيذ myarr[j] () وسيتم تشكيل الإغلاق.
لنأخذ مثالًا آخر
على الدالة a(){. وظيفة ب () { فار بب = 234 console.log(aaa); } فار أأ = 123 العودة ب // ب ولد في أ، ولكن تم حفظه} فارجلوب = 100 فار التجريبي = أ ()نستخدم أولاً التجميع المسبق لتحليل كود
العرض التوضيحي()
. أولاً، حدد كائن GO العام وابحث عن إعلان المتغير العام، وسيتم استخدام تعريف المتغير كاسم سمة GO غير محدد. ابحث عنه في الإعلان العام، يتم استخدام اسم الوظيفة كاسم السمة لكائن GO، ويتم تعيين القيمة إلى نص الوظيفة. في هذا الوقت يجب أن يكون GO{ glob: unتعريف--->100; الوظيفة a {} }، وأخيرًا، قم بترجمة الوظيفة b مسبقًا في الوظيفة a لإنشاء AO لـ b { b: unified--->234}؛ في هذا الوقت، ترتيب سلسلة النطاق هو 1. كائن AO للوظيفة b؛ 2. كائن AO للوظيفة أ؛ 3. كائن GO العالمي. عندما نطبع aaa في الدالة b، نبدأ من أعلى سلسلة النطاق. إذا لم يكن هناك aaa في كائن AO للدالة b، فسوف نبحث لأسفل على طول سلسلة النطاق للعثور على AO لوظيفة المستوى الثاني a. الهدف هو إيجاد قيمة aaa كـ 123 وإخراج النتيجة.
إذا لم نحللها من منظور التجميع المسبق، فسنعتقد أن aaa يجب أن يبلغ عن خطأ في هذا الوقت عند تنفيذ var demo = a()، عند انتهاء تنفيذ الوظيفة، فإن كائن AO يتوافق مع ذلك يجب تدمير a وفقًا للتحليل المنطقي: عندما نقوم بتنفيذ العرض التوضيحي، يجب أن تقوم سلسلة النطاق بإنشاء كائن AO وكائن GO لـ b. في هذا الوقت، لا يوجد سوى كائن AO لـ b ولا يوجد كائن AO لـ a. لا ينبغي طباعة قيمة aaa، ولكن في هذا الوقت قيمة aaa هي 123، مما يعني أن كائن AO الخاص بـ a لم يتم إتلافه، فلماذا؟ والسبب هو أنه تم إنشاء الإغلاق هنا عند اكتمال تنفيذ var demo = a()، ستسألك آلية جمع البيانات المهملة، يا أخي، أعتقد أنك قد انتهيت من تنفيذها ولكن في هذا الوقت، لم يتمكن الدالة "أ" إلا من هز رأسه بلا حول ولا قوة وقال، يا أخي، لست متأكدًا مما إذا كنت قد أكملت التنفيذ، لقد قمت بإنشاء "ب"، لكن "ب" ليس تحت سيطرتي، فأنا كذلك لست متأكدًا مما إذا كان قد تم استدعاء b، لذلك لست متأكدًا مما إذا كنت قد أكملت التنفيذ أم لا. نظرًا لأنك لا تعرف، فلن أقوم بإعادة تدوير الأشياء غير المكتملة. يجب أن أبلغ عن خطأ، لذلك لا يتم إعادة تدوير كائن AO في هذا الوقت.
أعتقد أنه من خلال هذين المثالين، لديك بالفعل فهم عام لعمليات الإغلاق. بعد ذلك، دعونا نتحدث عن وظائف عمليات الإغلاق.
وظيفة
الإغلاق هي
- تنفيذ المتغيرات العامة على سبيل المثال: يمكن تخزين المتراكم (3.js)
- مؤقتًا
- لتحقيق التغليف والخصخصة
- والتطوير المعياري للسمات لمنع تلوث المتغيرات العالمية.
3.js).
عدد فار = 0 إضافة وظيفة () { عدد الإرجاع++ } console.log(add()); console.log(add()); console.log(add());
هذا رمز تراكم شائع نسبيًا، ولكن إذا طلبت منك الشركة أثناء فترة التدريب أو حتى في العمل، تغليف المجمع في رمز معياري، ففي هذا الوقت، من أجل الوحدة نحاول تجنب تعريف المتغيرات العالمية قدر الإمكان، ولكن كيف يمكننا تحقيق ذلك دون تحديد المتغيرات العالمية في هذا الوقت يمكننا استخدام عمليات الإغلاق؟
إضافة وظيفة () { عدد فار = 0 وظيفة أ () { ++ عدد console.log(count); } العودة أ } فار الدقة = إضافة () الدقة () الدقة () // بعد انتهاء وظيفة الإضافة، لا يتم تدمير كائن AO للإضافة، لأنه بعد تنفيذ وظيفة الإضافة، يتم إرجاع إغلاق دون معرفة ما إذا كان قد تم استدعاؤه، بحيث يمكن تغليفه في وحدة نمطية دون استخدام المتغيرات العالمية.
، هذه بعض آرائي الشخصية حول عمليات الإغلاق ووظائفها، في الوقت الحالي، ليس لدي سوى فهم سطحي لعمليات الإغلاق الصحيحة، وإحراز التقدم معًا.