解决了
网络上有很多与此主题矛盾的信息。感谢@John,我设法弄清了闭包(如下所述)不是导致内存泄漏的原因,而且即使在IE8中,它们也不像人们所声称的那样普遍。实际上,我的代码中仅发生了1次泄漏,事实证明,修复起来并不难。
从现在开始,我对这个问题的回答将是:
IE8唯一一次泄漏的AFAIK是附加事件/在全局对象上设置处理程序的时间。 (window.onload
,window.onbeforeunload
,...)。为了解决这个问题,请参阅下面的答案。
巨大更新:
现在我完全迷路了……经过一段时间的研究,无论是新旧文章,我至少都面临着一个巨大的矛盾。而其中一位JavaScript大师(Douglas Crockford)说:
正如@freakish指出的那样,我的以下代码段类似于jQuery的内部工作原理,我对我的解决方案不会造成内存泄漏的感觉非常安全。同时,我找到了this MSDN page,其中Circular References with Closures
部分是我特别感兴趣的部分。下图几乎是我的代码工作原理的示意图,不是吗:
唯一的区别是,我的常识是不将事件侦听器附加到元素本身。所有相同的Douggie都非常明确:闭包不是IE中内存泄漏的源头。这种矛盾使我对谁是正确的一无所知。
我还发现IE9也无法完全解决泄漏问题(找不到链接ATM)。
最后一件事:我还了解到IE是在JScript引擎之外管理DOM的,这使我在基于ajax请求更改<select>
元素的子代时感到烦恼:
function changeSeason(e)
{
var xhr,sendVal,targetID;
e = e || window.event;//(IE...
targetID = this.id.replace(/commonSourceFragment/,'commonTargetFragment');//fooHomeSelect -> barHomeSelect
sendVal = this.options[this.selectedIndex].innerHTML.trim().substring(0,1);
xhr = prepareAjax(false,(function(t)
{
return function()
{
reusableCallback.apply(this,[t]);
}
})(document.getElementById(targetID)),'/index/ajax');
xhr({data:{newSelect:sendVal}});
}
function reusableCallback(elem)
{
if (this.readyState === 4 && this.status === 200)
{
var data = JSON.parse(this.responseText);
elem.innerHTML = '<option>' + data.theArray.join('</option><option>') + '</option>';
}
}
如果IE确实确实像不存在JScript引擎一样管理DOM,那么使用此代码未释放选项元素的可能性是多少?我特意添加了此代码段作为示例,因为在这种情况下,我将作为闭包范围一部分的变量作为全局函数的参数传递。我找不到有关此做法的任何文档,但是基于Miscrosoft提供的文档,它应该中断可能发生的所有循环引用,不是吗?警告:冗长的问题...(对不起)
我已经编写了一些相当大的JavaScript来在Web应用程序中进行Ajax调用。为了避免大量的回调和事件,我充分利用了事件委托(delegate)和关闭的优势。现在,我编写了一个让我想知道可能的内存泄漏的函数。尽管我知道IE> 8处理闭包的能力要比其前辈好得多,但公司的政策是始终支持IE 8。
在下面的示例中,我提供了关于here的示例,您可以找到一个类似的示例,尽管它不使用ajax,而是使用setTimeout,结果几乎相同。 (您当然可以跳过下面的代码,直接转到问题本身)
我想到的代码是这样的:
function prepareAjax(callback,method,url)
{
method = method || 'POST';
callback = callback || success;//a default CB, just logs/alerts the response
url = url || getUrl();//makes default url /currentController/ajax
var xhr = createXHRObject();//try{}catch etc...
xhr.open(method,url,true);
xhr.setRequestMethod('X-Requested-with','XMLHttpRequest');
xhr.setRequestHeader('Content-type','application/x-www-form-urlencoded');
xhr.setRequestHeader('Accept','*/*');
xhr.onreadystatechange = function()
{
callback.apply(xhr);
}
return function(data)
{
//do some checks on data before sending: data.hasOwnProperty('user') etc...
xhr.send(data);
}
}
除了onreadystatechange
回调,所有其他内容都很简单明了。直接绑定(bind)处理程序时,我注意到IE的一些问题:xhr.onreadystatechange = callback;
,因此是匿名函数。不知道为什么,但是我发现这是使其工作最简单的方法。就像我说的那样,我使用了很多事件委托(delegate),因此可以想象,访问触发ajax调用的实际元素/事件可能会很有用。因此,我有一些如下所示的事件处理程序:
function handleClick(e)
{
var target,parent,data,i;
e = e || window.event;
target = e.target || e.srcElement;
if (target.tagName.toLowerCase() !== 'input' && target.className !== 'delegateMe')
{
return true;
}
parent = target;
while(parent.tagName.toLowerCase() !== 'tr')
{
parent = parent.parentNode;
}
data = {};
for(i=0;i<parent.cells;i++)
{
data[parent.cells[i].className] = parent.cells[i].innerHTML;
}
//data looks something like {name:'Bar',firstName:'Foo',title:'Mr.'}
i = prepareAjax((function(t)
{
return function()
{
if (this.readyState === 4 && this.status === 200)
{
//check responseText and, if ok:
t.setAttribute('disabled','disabled');
}
}
})(target));
i(data);
}
如您所见,onreadystatechange
回调是函数的返回值,该函数在调用回调时提供对target
元素的引用。多亏了事件委托(delegate),当我决定将其从DOM中删除时(我有时这样做),我不再需要担心可能绑定(bind)到该元素的事件。但是在我看来,对于IE的JScript引擎及其垃圾回收器,回调函数的调用对象可能证明太多了:
我已经用FF和chrome测试了这个“构造”。它在那里工作得很好,但是这种关闭时的关闭调用栈会在关闭时(每次传递对DOM元素的引用)在IE(尤其是IE9之前的版本)中成为问题吗?
不,我不会使用jQuery或其他库。我喜欢纯JS,并且想尽可能多地了解这种被严重低估的语言。这些代码片段不是实际的复制粘贴示例,而是为IMO提供了一个很好的表示形式,说明了我在整个脚本中如何使用委派,闭包和回调。因此,如果某些语法不太正确,请随时进行更正,但是,这当然不是问题所在。
最佳答案
我曾经在非浏览器EcmaScript(err .. JScr ... JavaScript)项目上与Microsoft的JavaScript的前程序管理器一起工作。我们对闭包进行了长时间的讨论。最后,关键是它们难于使用GC,并非没有可能。我必须阅读DC关于MS如何“错误”地指出闭包会导致内存泄漏的讨论-因为在IE的较早实现中,闭包无疑是有问题的,因为它们很难用MS实现来垃圾回收。我感到奇怪的是,雅虎的一个人试图告诉MS架构师,他们代码的一个已知问题在其他地方。我很欣赏他的工作,但我看不出他在那里有什么基础。
请记住,您上面引用的文章是针对IE6的,因为IE7在撰写本文时仍处于开发阶段。
顺便说一句-感谢上帝,IE6已经死了(不要让我去挖掘葬礼图片)。虽然,不要忘记它的遗产...我还没有看到任何人提出可靠的论据,认为它在发布的第一天并不是世界上最好的浏览器-问题是他们赢得了浏览器大战。因此,这相当于他们历史上最大的失误之一-他们随后解雇了该团队,并且停滞了将近5年。 IE团队从事漏洞修复的人员只有4到5个,这造成了巨大的人才流失,并大大落后于曲线。当他们重新雇用团队并意识到他们在哪里时,他们已经落后了几年,因为处理单调的代码库的工作量越来越小,没人真正了解了。这是我作为公司内部人员的观点,但与该团队没有直接关系。
还要记住,IE从来没有针对闭包进行优化,因为没有ProtoypeJS(哎呀,没有Rails),而jQuery在Resig的脑海中几乎是一线曙光。
在撰写本文时,他们还针对具有256兆RAM的RAM的计算机,这些RAM也尚未报废。
在让我阅读了整本问题书之后,我认为将这一历史课交给您是很公平的。
最后,我的意思是,您引用的是过时的 Material 。是的,请避免在IE6中关闭,因为它们会导致内存泄漏-但是IE6中没有关闭?
最后,MS已经解决并继续解决这个问题。即使在那时,您也要进行某种程度的关闭。
我知道他们在IE8方面在这方面做了大量工作(因为我毫不犹豫的项目使用了非实时标准JavaScript引擎),并且这项工作一直持续到IE9/10中。 StatCounter(http://gs.statcounter.com/)认为IE7的市场份额已从一年前的6%下降到1.5%,并且在开发"new"网站时,IE7的相关性越来越低。您还可以为NetScape 2.0进行开发,该版本引入了JavaScript支持,但是这样做只会稍微减少一点。
真的...不要为了引擎不存在而过度优化。