下面的例子只是一个例子,我知道当用户点击div块时我不需要一个对象来显示警告框,但这只是一个简单的例子来解释编写JS时经常发生的情况代码。
在下面的示例中,我使用全局可见的对象数组来保持对每个新创建的HelloObject 的引用,这样在单击div块时调用的事件可以使用arry中的引用调用HelloObject的公共函数hello()。
第1看看代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<title>Test </title>
<script type="text/javascript">
/*****************************************************
Just a cross browser append event function, don't need to understand this one to answer my question
*****************************************************/
function AppendEvent(html_element, event_name, event_function) {if(html_element) {if(html_element.attachEvent) html_element.attachEvent("on" + event_name, event_function); else if(html_element.addEventListener) html_element.addEventListener(event_name, event_function, false); }}
/******************************************************
Just a test object
******************************************************/
var helloobjs = [];
var HelloObject = function HelloObject(div_container)
{
//Adding this object in helloobjs array
var id = helloobjs.length; helloobjs[id] = this;
//Appending click event to show the hello window
AppendEvent(div_container, 'click', function()
{
helloobjs[id].hello(); //THIS WORKS!
});
/***************************************************/
this.hello = function() { alert('hello'); }
}
</script>
</head><body>
<div id="one">click me</div>
<div id="two">click me</div>
<script type="text/javascript">
var t = new HelloObject(document.getElementById('one'));
var t = new HelloObject(document.getElementById('two'));
</script>
</body></html>
为了获得相同的结果,我可以简单地替换代码
//Appending click event to show the hello window
AppendEvent(div_container, 'click', function()
{
helloobjs[id].hello(); //THIS WORKS!
});
使用此代码:
//Appending click event to show the hello window
var me = this;
AppendEvent(div_container, 'click', function()
{
me.hello(); //THIS WORKS TOO AND THE GLOBAL helloobjs ARRAY BECOMES SUPEFLOUS!
});
因此会使 helloobjs数组超级丰富。
我的问题是:你认为第二个选项是否会在IE上创建memoy泄漏或奇怪的特定引用可能会导致浏览器变慢或中断?
我不知道如何解释,但是作为C / C ++编码器来自背景,以第二种方式进行听起来像是某种循环引用,可能在某些时候破坏记忆。
我还在互联网上阅读了关于IE闭包内存泄漏问题http://jibbering.com/faq/faq_notes/closures.html(我不知道它是否在IE7中得到修复,如果是,我希望它不会在IE8中再次出现)。
由于
答案 0 :(得分:2)
除了:
var HelloObject = function HelloObject(div_container)
通常尽量不要使用命名内联函数表达式。它通常不会让你得到任何东西,并且它们在JScript(IE)中存在严重问题。使用function HelloObject() { ... }
语句或var HelloObject= function() { ... };
匿名表达式。
你认为第二个选项会在IE上创建memoy泄漏
“创建”?不,您现有的代码已经在IE上泄露了。
您应用于click事件的侦听器具有闭包,保留对父函数作用域的引用。在这两个示例中,div_container
对象都在该范围内,因此您有一个循环引用:
div -> onclick ->
listener function -> parent scope ->
parent function -> reference to div
包含本机JS对象和宿主对象(例如div,DOM节点)混合的引用循环是导致IE6-7内存泄漏的原因。
要阻止这种情况发生,您可以将点击功能定义拉出父项:
function HelloObject(div_container) {
AppendEvent(div_container, 'click', HelloObject_hello);
}
function HelloObject_hello() {
alert('hello');
}
现在没有关闭,div_container
没有被记住,并且没有循环/泄漏。但是,hello
函数只是一个函数,不知道它属于哪个div_container
(除了查看它点击的event
/ this
之外)。
更典型的是,你需要记住div,或者,如果你是以对象的方式做事,this
:
function HelloObject(element) {
this.element= element;
AppendEvent(element, 'click', this.hello.bind(this));
}
HelloObject.prototype.hello= function() {
alert('Hello, you clicked on a '+this.element);
};
当然会带回参考循环:
element -> onclick
bound hello function -> bound this
Helloer instance -> 'element' member
reference to element
你真的关心这种refloop吗?也许不是。它只影响IE6-7;它在IE6中很糟糕,因为在您退出浏览器之前内存没有被回馈,但是随后有一种越来越多的想法认为仍然使用IE6的人应该得到他们得到的一切。
在IE7上,内存泄漏会一直存在,直到你离开页面为止,所以它只对你长时间运行的页面很重要,你会丢弃旧的HelloObject并反复绑定新的HelloObjects。 (在这种情况下,一个不丢弃旧值的HelloObjects线性数组本身也会出现内存泄漏,直到页面卸载为止。)
如果你真的在乎,因为你正在为一些运行IE6而且没有人关闭浏览器的肮脏公司工作,那么(a)我的哀悼,(b)是的,你确实必须实施像您拥有的查找对象,充当DOM节点和您用作事件侦听器的Function对象之间的解耦层。
某些框架有自己的事件解耦。例如,jQuery将事件处理函数附加到数据查找,由一个id放入每个Element对象的id;这使得它可以免费解耦,尽管它确实存在问题......
如果您使用的是纯JavaScript,这里有一些示例帮助程序代码。
// Event binding with IE compatibility, and decoupling layer to prevent IE6-7
// memory leaks
//
// Assumes flag IE67 pre-set through eg. conditional comments
//
function EventTarget_addEventListener(target, event, listener) {
if ('addEventListener' in target) {
target.addEventListener(event, listener, false);
} else if ('attachEvent' in target) {
if (IE67)
listener= listener.decouple();
target.attachEvent('on'+event, listener);
}
}
Function.decouple_bucket= {};
Function.decouple_factory= function() {
function decoupled() {
return Function.decouple_bucket[decoupled.decouple_id].apply(this, arguments);
}
return decoupled;
};
Function.prototype.decouple_id= null;
Function.prototype.decouple= function() {
var decoupled= Function.decouple_factory();
do {
var id= Math.floor(Math.random()*(Math.pow(2, 40)));
} while (id in Function.decouple_bucket);
decoupled.decouple_id= id;
Function.decouple_bucket[id]= this;
return decoupled;
};
Function.prototype.release= function() {
delete _decouple_bucket[this.decouple_id];
};
if (IE67) {
EventTarget_addEventListener(window, 'unload', function() {
Function.decouple_bucket.length= 0;
});
}
// Add ECMA262-5 method binding if not supported natively
//
if (!('bind' in Function.prototype)) {
Function.prototype.bind= function(owner) {
var that= this;
if (arguments.length<=1) {
return function() {
return that.apply(owner, arguments);
};
} else {
var args= Array.prototype.slice.call(arguments, 1);
return function() {
return that.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments)));
};
}
};
}
在完成所有繁琐的管道之后,您可以说:
function HelloObject(element) {
this.element= element;
EventTarget_addEventListener(element, 'click', this.hello.bind(this));
}
HelloObject.prototype.hello= function() {
alert('Hello, you clicked on a '+this.element);
};
可能会导致浏览器变慢或中断???
不,在谈论refloops时,我们只是担心内存泄漏(主要是在IE中)。
答案 1 :(得分:0)
答案在于问题的正文。由于你的C ++背景,你似乎很奇怪。第二个选项是Javascript-ish的方式。