我只是不能为我的生活弄清楚Internet Explorer中的内存泄漏。
insertTags
simple接受字符串str
,并将每个单词放在HTML的开始和结束标记内(通常是锚标记)。 transliterate
用于阿拉伯数字,并用&#.. n替换正常数字0-9;他们的阿拉伯语对应的XML身份。
fragment = document.createDocumentFragment();
for (i = 0, e = response.verses.length; i < e; i++)
{
fragment.appendChild((function(){
p = document.createElement('p');
p.setAttribute('lang', (response.unicode) ? 'ar' : 'en');
p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
try { return p } finally { p = null; }
})());
}
params[0].appendChild( fragment );
fragment = null;
我会喜欢除MSDN和about.com以外的一些链接,因为他们都没有充分向我解释为什么我的脚本会泄漏内存。我确信这是问题所在,因为没有它,一切都会快速运行(但没有显示)。
我读过做很多DOM操作可能很危险,但for循环最多286次(古兰经中最长的surah 2中的经文#)。
* IE7和IE8中的内存泄漏,不确定大约6,但在Safari 4,FF 3.6,Opera 10.5,Chrome 5中完美运行...... *
答案 0 :(得分:6)
变量的范围是函数,而不是/ else / for / while / etc。块。每次打电话
fragment.appendChild((function() { ...
您正在创建一个新功能(新范围)。此新函数引用i
和response
变量。所以现在,i
和response
的范围是外部函数和新函数。
这还不足以泄漏内存。 (i
和response
是在新函数完成后超出范围的正常变量)
但是,你在新函数中创建一个p
DOM元素,并在外部函数中引用它(将它作为参数返回到fragment.appendChild
调用)。现在考虑一下:你有外部作用域fragment
引用从内部作用域创建的p
DOM,它需要使用外部作用域中的i
和response
变量首先创建DOM元素。
fragment
和p
DOM对象各自具有相互引用。尽管您尝试通过将变量指针置零来将引用计数置零,但p=null
和fragment = null
将不会删除所有引用。 fragment
仍然引用了内部p
,它仍然引用了外部response
变量。由于剩下的循环依赖性,这两个“范围”永远不会被垃圾收集。
任何人,如果我犯了任何错误,请纠正我。
至于解决方案,只是不要使用内部函数!
fragment = document.createDocumentFragment();
for (var i = 0, var e = response.verses.length; i < e; i++)
{
var p = document.createElement('p');
p.setAttribute('lang', (response.unicode) ? 'ar' : 'en');
p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
fragment.appendChild(p);
}
params[0].appendChild( fragment );
答案 1 :(得分:2)
虽然答案已被接受,但我认为这也可以完成这项工作:
var fragment = document.createDocumentFragment();
for (var i = 0, e = response.verses.length; i < e; i++) {
fragment.appendChild((function(p){ // Create a scope here itself :)
p = document.createElement('p'); // ?? without above, it was a global scope
p.setAttribute('lang', (response.unicode) ? 'ar' : 'en');
p.innerHTML = ((response.unicode) ? (response.surah + ':' + (i+1)).transliterate() : response.surah + ':' + (i+1)) + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
try { return p } finally { p = null; }
})());
}
params[0].appendChild( fragment );
fragment = null;
内存泄漏的原因:在匿名函数中创建并返回一个闭包,然后保持活动,但不是垃圾回收,因为fragment
正在使用它
因此解决方案可以像提供词汇范围一样简单,如上所示
答案 2 :(得分:0)
如果从链接中删除onclick属性,它是否会泄漏?
您可以尝试删除重复的onclick并将其替换为事件委派。
此外,您的所有变量似乎都在全局范围内 - 这不应该导致您看到的问题,但是无论如何都应该解决这个问题。
答案 3 :(得分:-2)
我无法告诉你为什么 IE可能会泄漏内存,但这段代码的功能非常复杂。这条线似乎非常可疑和多余:try { return p } finally { p = null; }
。
如何简化它并确定变量的范围:
var fragment = document.createDocumentFragment();
var p, t;
for (var i = 0; i < response.verses.length; i++)
{
p = document.createElement('p');
if (response.unicode) {
p.setAttribute('lang', 'ar');
t = (response.surah + ':' + (i+1)).transliterate();
} else {
p.setAttribute('lang', 'en');
t = response.surah + ':' + (i+1);
}
p.innerHTML = t + ' ' + insertTags(response.verses[i], '<a href="#" onclick="window.popup(this);return false;" class="match">', '</a>');
fragment.appendChild(p);
}
params[0].appendChild(fragment);
fragment = p = t = null; // likely unnecessary if they go out of scope anyway
尽管如此,这仍然是很多DOM操作,在缓慢的Javascript引擎上可能需要一段时间。