2011-02-04 8 views
11

निम्नलिखित कोड रिसाव क्यों करता है?इंटरनेट एक्सप्लोरर 8 में यह रिसाव क्यों है?

for (var i = 0; i < 100; i++) { 
    var item = {}; 
    item.elem = document.createElement('div'); 
    document.body.appendChild(item.elem); 
    item.addEvent = function(name,listener) { 
     var self = this; 
     var wrappedListener = function() { 
      return listener.apply(self,arguments); 
     } 
     //Uh-oh creating a circular reference here! 
     //The wrappedListener has a closure on self and therefore on item.elem. 
     addEvent(this.elem,name,wrappedListener); 
     return wrappedListener; 
    } 
    var wrap = item.addEvent('eventName',listen); 

    //Now remove the eventHandler - this should free up the circular reference. 
    removeEvent(item.elem, 'eventName', wrap); 
    if (item.elem.parentNode) { 
     item.elem.parentNode.removeChild(item.elem); 
    } 
    //item.elem = null; //With this also un-commented, the leak disappears. 
    //The fact that I have to null item.elem tells me that something is holding 
    //a reference to item, and therefore elem. Setting elem to null fixes the 
    //problem, but since I am removing the event handler, I don't think this 
    //should be required. 
} 

नोट: addEvent और removeEvent सिर्फ इंटरनेट एक्सप्लोरर और अन्य ब्राउज़रों के बीच सार attachEvent/addEventListener मतभेद रहे हैं।

मैं एक jsFiddle परियोजना जो समस्या को दर्शाता है बनाया। बस इंटरनेट एक्सप्लोरर 8 को फायर करें और इसे टास्क मैनेजर या प्रोसेस एक्सप्लोरर में देखें। इसके अलावा, आप addEvent और removeEvent की परिभाषा देखेंगे।

http://jsfiddle.net/rJ8x5/34/

संपादित करें: ठीक है, मैं निम्नलिखित समाधान के साथ आया था। यह सुंदर नहीं है, लेकिन यह काम करता है! http://jsfiddle.net/rJ8x5/43/

var item = {}; 
item.elem = document.createElement('div'); 
document.body.appendChild(item.elem); 
item.addEvent = function(name,listener) { 
    var wrappedListener = function() { 
     //Access the scope through the callee properties. 
     return listener.apply(arguments.callee.scope, arguments); 
    } 
    addEvent(this.elem,name,wrappedListener); 
    //Save the scope not as a closure, but as a property on the handler. 
    wrappedListener.scope = this 
    return wrappedListener; 
} 
var wrap = item.addEvent('eventName',listen); 
removeEvent(item.elem, 'eventName', wrap); 
//Force the circular reference to GO AWAY. 
wrap.scope = null 
if (item.elem.parentNode) { 
    item.elem.parentNode.removeChild(item.elem); 
} 
//item.elem = null; //No longer needed. 
+0

मुझे पता है कि आईई एक बग जहां यह देशी वस्तुओं और javascript ऑब्जेक्ट के लिए अलग से कचरा संग्रहण, का प्रयोग करेंगे तो किसी भी समय आप एक चक्र javascript ऑब्जेक्ट और देशी वस्तुओं के बीच, न तो कचरा कलेक्टर यह साफ कर सकता है के रूप में किया करते थे। क्या यह हो सकता है? मैं सोच रहा हूं कि आइटम आइटम item.elem को इंगित करता है जो एक div है जिसमें हैंडलर हैं जो आइटम को वापस संदर्भित करते हैं। – btilly

+0

- वास्तव में आपके प्रश्न के अनुरूप नहीं है, लेकिन आप 'listener.apply (आइटम, तर्क) 'का उपयोग कर' self' चर को खत्म कर सकते हैं। :-) –

+0

पूरी तरह से! लेकिन यह वास्तव में एक वर्ग का एक बहुत सरलीकृत संस्करण है जिसे मैंने लिखा है जहां एडवेन्ट विधि के पास इतनी आसानी से पहुंच नहीं है - इसलिए var self = यह – jordancpaul

उत्तर

5

समस्या घटनाओं (के रूप में लगभग इंटरनेट एक्सप्लोरर में हमेशा की तरह, BTW) है।

http://jsfiddle.net/rJ8x5/39/ पर देखो और ध्यान दें कि किस यह कचरा ठीक एकत्र करता है।

आप वृत्तीय संदर्भ बनाते समय आप घटनाओं देते रहे हैं। Circular references to DOM objects on an HTML page cause a memory leak में इसके बारे में और पढ़ें।

+0

आह! हां, क्योंकि फ़ंक्शन बंद होने पर डीओएम तत्व का संदर्भ लेता है, और इसलिए जब यह जेस्क्रिप्ट द्वारा डीओएम तत्व लीक द्वारा एकत्रित कचरा होता है। क्या वह सही है? – Pointy

+0

@Pointy: हाँ, यह सब ईवेंट हैंडलर की scoping के बारे में है, या कहें कि गुंजाइश :) –

+0

में फंस डोम संदर्भों के बारे में मैं IE के वृत्तीय संदर्भ संबंधित मेमोरी लीक के बारे में पता कर रहा हूँ। हालांकि, यहां बिंदु यह है कि मैं विशेष रूप से ईवेंट हैंडलर _removing_ हूं। क्या यह परिपत्र संदर्भ को तोड़ना नहीं चाहिए? – jordancpaul

1

कोड लीक क्योंकि आप अनुलग्नक को अनुचित रूप से कॉल कर रहे हैं - माइक्रोसॉफ्ट के documentation जोर देकर कहते हैं कि ईवेंट का नाम मानक डीएचटीएम घटना है।

यदि आप 'eventName' से 'click' बदलते हैं, तो यह रिसाव नहीं होता है।

एक और अधिक मजबूत समाधान 'on'+eventname in domElement की जाँच करें और घटना संलग्न करने के लिए करता है, तो यह झूठ है मना करने के लिए अपने घटना संलग्न कोड बदलने के लिए किया जाएगा।

संबंधित मुद्दे