الإغلاق هو صعوبة في لغة JavaScript وميزةها. تعتمد العديد من التطبيقات المتقدمة على عمليات الإغلاق للتنفيذ. لقد تعرضت لمفهوم الإغلاق لفترة طويلة ، لكنني كنت في حيرة من أمري ولم أتمكن من فهم ماهية إغلاق JavaScript وما هي مفيدة. اليوم رأيت مقالًا عن إغلاق JavaScript (الرابط الأصلي) على الإنترنت. تم شرحه جيدًا. لقد فهمت الآن تمامًا أن عمليات إغلاق JavaScript أمر سحري والغرض من عمليات الإغلاق. سأكتبها هنا لأشاركها معك. آمل أن يتمكن الأصدقاء الذين لا يفهمون من إغلاق JavaScript من فهم الإغلاق بعد قراءتها! معظم المحتوى التالي يأتي من النص الأصلي. لقد أضفت بعض تعليقات التعليمات البرمجية ، وعرضات التشغيل ، وقليل من التعديل إلى النص الأصلي لسهولة الفهم!
1. نطاق المتغيرات
لفهم عمليات الإغلاق ، يجب عليك أولاً فهم النطاق المتغير الخاص لـ JavaScript.
في JavaScript ، ينقسم نطاق المتغيرات إلى نوعين: المتغيرات العالمية والمتغيرات المحلية.
في JavaScript ، يمكن قراءة المتغيرات العالمية مباشرة داخل وظيفة.
var n = ؛ // تحديد المتغير العالمي nfunction f () {Alert ("الوصول إلى المتغير العالمي n ، n ="+n) ؛ // الوصول إلى المتغير العالمي n} f () ؛ //نتائج التشغيل:
ولكن العكس غير ممكن ، لا يمكن قراءة المتغيرات المحلية داخل الوظيفة خارج الوظيفة.
الدالة f () {var n = ؛ // تحديد تنبيه المتغير المحلي n} ("الوصول إلى المتغير المحلي n خارج الوظيفة ، n ="+n) ؛ // الوصول إلى المتغير المحلي n خارج الوظيفة ، خطأ: n غير محددنتائج التشغيل:
هناك مكان يجب ملاحظته هنا. عند إعلان المتغيرات داخليًا ، يجب عليك استخدام أمر VAR. إذا لم يكن كذلك ، فهو في الواقع متغير عالمي معلن!
الدالة f () {n = ؛} f () ؛ التنبيه ("n لم يتم إعلانها مع var داخل وظيفة f1 ، في هذا الوقت n هو متغير عالمي ، /r /n إثبات: n ="+n+"، نتيجة window.n == n is:"+(window.n == n)) ؛نتائج التشغيل:
2. كيف تقرأ المتغيرات المحلية من الخارج؟
لأسباب مختلفة ، نحتاج أحيانًا إلى الحصول على متغيرات محلية داخل الوظيفة. ومع ذلك ، كما ذكرنا سابقًا ، في ظل الظروف العادية ، لا يمكن القيام بذلك ولا يمكن تحقيقه إلا من خلال الحلول.
وهذا هو تحديد وظيفة أخرى داخل الوظيفة.
الدالة f () {var n = ؛ // المتغير المحلي n داخل f function // تحديد وظيفة f function f () {// insed f function ، Alert (n) ؛ //}}في الكود أعلاه ، يتم تضمين الوظيفة F2 داخل الوظيفة F1 ، وجميع المتغيرات المحلية داخل F1 مرئية لـ F2. لكن العكس غير ممكن. المتغيرات المحلية داخل F2 غير مرئية لـ F1. هذا هو بنية "نطاق السلسلة" الفريدة للغة JavaScript. ستبدو الكائنات الفرعية مستوى لأعلى حسب مستوى متغيرات جميع الكائنات الأصل. لذلك ، فإن جميع متغيرات الكائن الأصل مرئي للكائن الطفل ، وإلا فإنه غير صحيح. نظرًا لأن F2 يمكنه قراءة المتغيرات المحلية في F1 ، طالما يتم استخدام F2 كقيمة الإرجاع ، ألا يمكننا قراءة متغيراتها الداخلية خارج F1؟ قد يكون لدى بعض الناس أسئلة. F2 هي وظيفة ، كيف يمكن إرجاعها كقيمة إرجاع وظيفة F1؟ في الواقع ، لا بأس. اسم الوظيفة في JavaScript نفسه متغير ، بحيث يمكن أيضًا استخدام الوظيفة كمتغير عادي. أي أنه لا يمكن نقل وظيفة واحدة فقط إلى وظيفة أخرى مثل تمرير المعلمات ، ولكن يمكن أيضًا إرجاع وظيفة واحدة كقيمة إرجاع وظيفة أخرى.
الدالة f () {var n = ؛ // متغير محلي n // f وظيفة تم إعلانها داخل f function f () {Alert (n) ؛ } return f ؛ // استخدم f function كقيمة إرجاع function} var result = f () ؛ // تسمى قيمة الإرجاع بعد F هي وظيفة f ، والنتيجة هي f function result () ؛ // 999 ، استدعاء وظيفة F2نتائج التشغيل:
3. مفهوم الإغلاق
وظيفة F2 في القسم السابق من الرمز هي الإغلاق. تعريف "الإغلاق" في مختلف الوثائق المهنية مجردة للغاية. على سبيل المثال ، هناك تعريف إغلاق: "إغلاق JavaScript هو متغير يحصل عليه من وظيفة المستوى السابق أو النطاق في نطاق آخر ، ولن يتم تدمير هذه المتغيرات مع اكتمال تنفيذ وظيفة المستوى السابق." من الصعب بالنسبة لي أن أفهم مثل هذا التعريف الإغلاق. أفهم أن الإغلاق هو وظيفة يمكنها قراءة المتغيرات داخل وظائف أخرى. نظرًا لأنه في لغة JavaScript ، يمكن فقط للوظائف الفرعية داخل الوظائف قراءة المتغيرات المحلية ، يمكن فهم عمليات الإغلاق ببساطة على أنها "وظائف محددة داخل وظيفة". لذلك ، في جوهرها ، الإغلاق هو جسر يربط داخل وخارج الوظيفة.
4. الغرض من الإغلاق
يمكن استخدام عمليات الإغلاق في العديد من الأماكن. إنه له أكبر استخدامات ، أحدهما هو أن المتغيرات الموجودة داخل الوظيفة يمكن قراءة كما ذكر أعلاه ، والآخر هو أن قيم هذه المتغيرات يتم الاحتفاظ بها دائمًا في الذاكرة.
كيف تفهم هذه الجملة؟ يرجى الاطلاع على الرمز أدناه.
الدالة f () {var n = ؛ // nadd هو متغير عالمي لم يتم الإعلان عنه باستخدام var. يشير هذا المتغير الآن إلى وظيفة مجهولة معلنة داخل وظيفة f nadd = function () {n+=} function f () {Alert (n) ؛ } إرجاع f ؛ } var result = f () ؛ // النتيجة هي نتيجة f function f () ؛ // الدعوة الأولى لوظيفة النتيجة NADD () ؛ // nadd تمثل وظيفة مجهولة معلنة داخل وظيفة f ، NADD () هي نتيجة دالة مجهولة المصدر () ؛ // الدعوة الثانية للدالة 1000نتائج التشغيل:
في هذا الرمز ، النتيجة هي في الواقع وظيفة الإغلاق F2. يتم تشغيله مرتين في المجموع ، والقيمة الأولى هي 999 والقيمة الثانية هي 1000. وهذا يثبت أن المتغير المحلي N في الوظيفة F1 قد تم الاحتفاظ به في الذاكرة ولم يتم مسحه تلقائيًا بعد استدعاء F1.
لماذا هذا يحدث؟ والسبب هو أن F1 هو الدالة الأصل لـ F2 ، ويتم تعيين F2 لمتغير عالمي ، والذي يتسبب في أن يكون F2 دائمًا في الذاكرة ، ويعتمد وجود F2 على F1. لذلك ، يكون F1 دائمًا في الذاكرة ولن يتم إعادة تدويره بواسطة آلية جمع القمامة بعد الانتهاء من المكالمة.
هناك نقطة أخرى جديرة بالملاحظة في هذا الرمز وهي أن السطر "nadd = function () {n+= 1}" يستخدم لأول مرة قبل NADD ، لذلك NADD هو متغير عالمي ، وليس متغيرًا محليًا. ثانياً ، قيمة NADD هي وظيفة مجهولة ، وهذه الوظيفة المجهولة نفسها هي أيضًا إغلاق ، لذلك فإن NADD تعادل مراسلة ، والتي يمكن أن تعمل على المتغيرات المحلية داخل الوظيفة خارج الوظيفة.
5. ملاحظات حول استخدام الإغلاق
1) نظرًا لأن عمليات الإغلاق سوف تتسبب في تخزين جميع المتغيرات في الوظيفة في الذاكرة ، ولا يمكن إساءة استخدام استهلاك الذاكرة ، ولا يمكن إساءة استخدامها ، وإلا فإنه سيؤدي إلى مشاكل في أداء صفحة الويب وقد يؤدي إلى تسرب الذاكرة في IE. الحل هو حذف جميع المتغيرات المحلية التي لا تستخدم قبل الخروج من الوظيفة.
2) سيغير الإغلاق قيمة المتغير داخل وظيفة الأصل خارج وظيفة الأصل. لذلك ، إذا كنت تستخدم وظيفة الأصل ككائن ، فاستخدم الإغلاق كطريقة عامة له ، واستخدم المتغير الداخلي كخاصية خاصة به ، فاحرص على عدم تغيير قيمة المتغير الداخلي لوظيفة الأصل في الإرادة.
6. أسئلة التفكير
إذا تمكنت من فهم نتائج الجري للقطعة التالية من التعليمات البرمجية ، فيجب اعتبارك فهم آلية التشغيل للإغلاق.
قصاصة الكود 1:
var name = "The Window" ؛ var object = {name: "كائن بلدي" ، getNameFunc: function () {return function () {return this.name ؛ } ؛ }} ؛ التنبيه (object.getNameFunc () () ()) ؛نتائج التشغيل:
قصاصة الكود الثاني:
var name = "The Window" ؛ var object = {name: "كائن بلدي" ، getNameFunc: function () {var that = this ؛ إرجاع وظيفة () {return that.name ؛ } ؛ }} ؛ التنبيه (object.getNameFunc () ()) ؛نتائج التشغيل:
تتم إضافة التعليقات التالية إلى الكود لتحليل نتائج تشغيل قصاصات الرمز أعلاه:
قصاصة الكود 1:
التحليل كما يلي:
/*في JavaScript ، ستصبح الكائنات العالمية JavaScript والوظائف العالمية والمتغيرات العالمية التي نعلنها تلقائيًا أعضاء في كائن النافذة. المتغيرات العالمية هي خصائص كائنات النوافذ. الوظائف العالمية هي طرق كائنات النوافذ. */var name = "The Window" ؛ // إعلان اسم متغير عالمي ، وفي هذا الوقت سيصبح الاسم المتغير العالمي تلقائيًا سمة لكائن النافذة // الإثبات: التنبيه ("window.name:"+window.name) ؛ الوقت سيصبح كائن الكائن المتغير العالمي تلقائيًا سمة لكائن Window var Object = {name: "كائن بلدي" ، // اسم السمة لكائن getNameFunc: function () {// getNameFunc من كائن كائن // ، فإن قيمة الإرجاع الخاصة بوظيفة getNameFunc هي التي تقع عليها الكائنات التي تقع فيها الكائن ، والكائن الذي يقدره الكائن ، والكائن الذي يقدر فيه الكائن ، أيها الكائن ، أيها الكائن ، أيها الكائن ، فإن الكائنات التي تقع ، إلى أي كائن. // إثبات أن هذا في الدالة المجهولة يمثل كائن نافذة بدلاً من objectalert ("هذه == نتيجة الكائن هي:"+(هذا == كائن)) ؛ التنبيه ("هذا == نتيجة النافذة هي:"+(هذا == نافذة)) ؛ إرجاع this.name ؛ // نظرًا لأن هذا يمثل كائن نافذة ، فإن This.name تصل بشكل طبيعي إلى اسم كائن النافذة "النافذة"} ؛ }} ؛ // proof: كائن الكائن العالمي هو سمة لتنبيه كائن النافذة ("window.object:"+window.object) ؛ Alert ("window.object.name:"+window.object.name) ؛/*بعد استدعاء طريقة getNameFunc ، يتم إرجاع طريقة مجهولة. في هذا الوقت ، يمثل Retfn طريقة مجهولة. الآن يعادل إعطاء الطريقة المجهولة اسم retfn. في هذا الوقت ، تصبح وظيفة RETFN تلقائيًا وظيفة لكائن النافذة*/var retfn = object.getNameFunc () ؛ Alert (RETFN ()) ؛ // استدعاء الطريقة المجهولة التي تم إرجاعها ، فمن يطلق على هذه الطريقة المجهولة المصدر؟ إنه كائن نافذة // دليل: وظيفة RETFN هي دالة لتنبيه كائن النافذة ("window.retfn ():"+window.retfn ()) ؛ // يمكنك استدعاء طريقة retfn في شكل window.retfn () (اسم الكائن.قصاصة الكود الثاني:
التحليل كما يلي:
var name = "The Window" ؛ // Global Variable Name // Global ObjectVar Object = {name: "My Object" ، getNameFunc: function () {/*ما هو الكائن الذي يمثل هذا في هذا الوقت؟ هذا يمثل كائن الكائن. أي كائن يستدعي الوظيفة التي يقع فيها هذا؟ يشير هذا إلى أي كائن تم تنفيذه = هذا ، ويمثل أيضًا كائن الكائن*/var that = this ؛ // وهو متغير محلي معلن في وظيفة getNameFunc // تثبت أن هذا في وظيفة getNameFunc يمثل كائن الكائن بدلاً من النافذة ("هذه == نتيجة الكائن هي:"+(this= object)) ؛ التنبيه ("نتيجة النافذة هذه == هي:"+(هذا == نافذة)) ؛ // إثبات أن هذا يمثل تنبيه كائن الكائن ("that == نتيجة الكائن هي:"+(that == object)) ؛ Return Function () {/*وهذا هو متغير محلي معلن في وظيفة getNameFunc. في ظل الظروف العادية ، بعد اكتمال استدعاء وظيفة getNameFunc ، يتم إعادة تدوير المتغير المحلي الذي سيتم إعادة تدويره بواسطة GC's JavaScript ، مما يحرر مساحة الذاكرة التي يشغلها المتغير المحلي ، ولكن الآن يمكن استخدامها بشكل طبيعي ولم يتم إعادة تدويرها. والسبب هو أن getNameFunc هو الدالة الأصل للدالة المجهولة. بعد استدعاء وظيفة getNameFunc ، سيتم إرجاع الوظيفة المجهولة وتخصيصها إلى Retfn متغير عالمي ، مما يؤدي إلى أن تكون الوظيفة المجهولة دائمًا في الذاكرة ، ويعتمد وجود الوظيفة المجهولة على وظيفة getNameFunc. لذلك ، فإن وظيفة getNameFunc دائمًا في الذاكرة ولن يتم إعادة تدويرها بواسطة آلية جمع القمامة بعد الانتهاء من المكالمة. نظرًا لأن وظيفة getNameFunc دائمًا في الذاكرة ، فإن المتغير المحلي الذي تم الإعلان عنه داخل وظيفة getNameFunc سيكون موجودًا دائمًا في الذاكرة. نظرًا لأنه موجود ، بالطبع يمكن استخدامه. */إرجاع that.name ؛ // الذي يمثل كائن الكائن ، بحيث يصل إلى اسم كائن الكائن بشكل طبيعي "كائن بلدي"} ؛ }} ؛ var retfn = object.getNameFunc () ؛ // بعد استدعاء طريقة getNameFunc ، يتم إرجاع طريقة مجهولة. في هذا الوقت ، يمثل RETFN طريقة مجهولة المصدر ، والتي تعادل الآن إعطاء الطريقة المجهولة أن يكون الاسم RETFN ALERT (RETFN ()) ؛أخيرًا ، أرفقت أيضًا مثالاً كتبته عندما تعلمت إغلاق JavaScript من قبل:
<script type = "text/javaScript"> function a () {var i = ؛ // إعلان المتغير المحلي i داخل الدالة a/إعلان الوظيفة الفرعية bfunction b () {Alert ("i ="+(++ i)) ؛ يشير إلى الوظيفة ب. ب. المتغير أنا يستخدم. بعد تنفيذ C () ، ستظهر نافذة لعرض قيمة I (المرة الأولى) وهذا الرمز ينشئ بالفعل إغلاقًا ، لأن المتغير C خارج الوظيفة A يشير إلى الوظيفة B الداخلية A. وهذا يعني: عندما تتم الإشارة إلى الوظيفة الداخلية للوظيفة A بواسطة وظيفة خارجية متغيرة ، يتم إنشاء ما يسمى "الإغلاق". بعد تنفيذ A وإعادته ، فإن الإغلاق يجعل آلية جمع القمامة JavaScript GC لن تعيد تدوير الموارد التي تشغلها A ، لأن تنفيذ الوظيفة الداخلية B لاحتياجات الاعتماد على المتغير في */A () ؛ // سيكون هناك بالتأكيد مساحة في الذاكرة. بعد تنفيذ A () ، ستقوم GC بإعادة تدوير مساحة الذاكرة المخصصة لـ i var c = a () ؛ // هذا الاستخدام ، لن يعامل GC على أنه قمامة و c () ؛ // مكافئة للاتصال b () ، والنتيجة هي: i = c () ؛ // النتيجة هي: i = c () ؛نتائج التشغيل:
المحتوى أعلاه هو التفسير التفصيلي لرمز إغلاق JavaScript (الإغلاق) لنقاط المعرفة JavaScript الملخصة (16) التي قدمها المحرر. آمل أن يكون ذلك مفيدًا للجميع!