ألا تدعم JavaScript عمليات الإغلاق بالمتغيرات المحلية؟[ينسخ]

StackOverflow https://stackoverflow.com/questions/643542

  •  22-07-2019
  •  | 
  •  

سؤال

هذا السؤال لديه بالفعل إجابة هنا:

أنا في حيرة شديدة بشأن هذا الرمز:

var closures = [];
function create() {
  for (var i = 0; i < 5; i++) {
    closures[i] = function() {
      alert("i = " + i);
    };
  }
}

function run() {
  for (var i = 0; i < 5; i++) {
    closures[i]();
  }
}

create();
run();

من وجهة نظري يجب طباعة 0،1،2،3،4 (أليس هذا هو مفهوم الإغلاق؟).

بدلا من ذلك فإنه يطبع 5،5،5،5،5.

لقد قمت بتجربة Rhino وFirefox.

هل يمكن لأحد أن يشرح لي هذا السلوك؟شكرا مقدما.

هل كانت مفيدة؟

المحلول

تم إصلاح إجابة جون عن طريق إضافة وظيفة مجهولة إضافية:

function create() {
  for (var i = 0; i < 5; i++) {
    closures[i] = (function(tmp) {
        return function() {
          alert("i = " + tmp);
        };
    })(i);
  }
}

التفسير هو أن نطاقات JavaScript هي على مستوى الوظيفة، وليست على مستوى الكتلة، وإنشاء إغلاق يعني فقط إضافة النطاق المتضمن إلى البيئة المعجمية للوظيفة المضمنة.

بعد انتهاء الحلقة، متغير مستوى الوظيفة i لديه القيمة 5, وهذا ما "تراه" الوظيفة الداخلية.


كملاحظة جانبية:يجب عليك الحذر من إنشاء كائنات دالة غير ضرورية، خاصة في الحلقات؛فهو غير فعال، وإذا كانت كائنات DOM متضمنة، فمن السهل إنشاء مراجع دائرية وبالتالي حدوث تسرب للذاكرة في Internet Explorer.

نصائح أخرى

أعتقد أن هذا قد يكون ما تريد:

var closures = [];

function createClosure(i) {
    closures[i] = function() {
        alert("i = " + i);
    };
}

function create() {
    for (var i = 0; i < 5; i++) {
        createClosure(i);
    }
}

الحل هو أن يكون لديك لامدا ذاتية التنفيذ لتغليف مصفوفتك.يمكنك أيضًا تمرير i كحجة إلى لامدا.قيمة i داخل لامدا ذاتية التنفيذ سوف تظلل قيمة i الأصلية وسيعمل كل شيء على النحو المنشود:

function create() {
    for (var i = 0; i < 5; i++) (function(i) {
        closures[i] = function() {
            alert("i = " + i);
        };
    })(i);
}

الحل الآخر هو إنشاء إغلاق آخر يلتقط القيمة الصحيحة لـ i ويعينها لمتغير آخر والذي "سيتم اكتشافه" في لامدا النهائية:

function create() {
    for (var i = 0; i < 5; i++) (function() {
        var x = i;

        closures.push(function() {
            alert("i = " + x);
        });
    })();
}

نعم عمليات الإغلاق تعمل هنا.في كل مرة تقوم فيها بتكرار الوظيفة التي تقوم بإنشائها، يتم الاستيلاء على i.كل وظيفة تقوم بإنشائها تشترك في نفس الشيء i.المشكلة التي تراها هي أنهم جميعًا يشتركون في نفس الشيء i كما أنهم يتشاركون في القيمة النهائية لـ i لأنه هو نفس المتغير الذي تم التقاطه.

يحرر: هذا المقال بواسطة السيد.يشرح Skeet عمليات الإغلاق بشيء من العمق ويعالج هذه المشكلة على وجه الخصوص بطريقة أكثر إفادة مما لدي هنا. ومع ذلك، كن حذرًا لأن الطريقة التي تتعامل بها Javascript وC# مع عمليات الإغلاق بها بعض الاختلافات الدقيقة. انتقل إلى القسم المسمى "مقارنة استراتيجيات الالتقاط:التعقيد مقابل القوة" لشرحه لهذه المسألة.

جون ريسيج تعلم جافا سكريبت المتقدمة يوضح هذا وأكثر.إنه عرض تقديمي تفاعلي يشرح الكثير عن JavaScript، والأمثلة ممتعة في القراءة والتنفيذ.

وفيه فصل عن الإغلاقات، و هذا المثال يشبه إلى حد كبير لك.

إليك المثال المكسور:

var count = 0; 
for ( var i = 0; i < 4; i++ ) { 
  setTimeout(function(){ 
    assert( i == count++, "Check the value of i." ); 
  }, i * 200); 
}

والإصلاح:

var count = 0; 
for ( var i = 0; i < 4; i++ ) (function(i){ 
  setTimeout(function(){ 
    assert( i == count++, "Check the value of i." ); 
  }, i * 200); 
})(i);

مجرد تحديد وظيفة داخلية، أو تخصيصها لبعض المتغيرات:

closures[i] = function() {...

لا يقوم بإنشاء نسخة خاصة من سياق التنفيذ بأكمله.لا يتم نسخ السياق حتى يتم نسخ أقرب وظيفة خارجية الخروج (عند هذه النقطة يمكن أن يتم جمع تلك المتغيرات الخارجية، لذلك من الأفضل أن نحصل على نسخة منها).

هذا هو السبب في أن التفاف وظيفة أخرى حول وظيفتك الداخلية يعمل - يقوم الرجل الأوسط بالفعل بالتنفيذ والخروج، مما يشير إلى الوظيفة الأعمق لحفظ نسخته الخاصة من المكدس.

إليك ما يجب عليك فعله لتحقيق النتيجة:

<script>
var closures = [];
function create() {  
    for (var i = 0; i < 5; i++) {   
        closures[i] = function(number) {      
        alert("i = " + number);   
        };  
    }
}
function run() {  
    for (var i = 0; i < 5; i++) {   
        closures[i](i); 
    }
}
create();
run();
</script>
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top