4

众所周知,当我们在 javascript 中创建类时,普通函数会返回类对象,但事件会返回事件对象,而类对象会丢失

function class(a){
 this.name=a;
 document.addEventListener('click',this.click,false);
 xhr.addEventListener('load',this.xhr,false);
 this.normal()
}
class.prototype={
 click:function(e){
  //e=event,this=theDocument //can't access class
 },
 xhr:function(e){
  //e=event,this=theXHR //can't access class
 },
 normal:function(e){
  //e=null,this=class
 }
}

将这些事件绑定到我们的班级的最佳方式是什么?

最好的方式是我的意思是没有或只是一个很小的参考,能够以本机方式删除事件(removeEventListener)并且绝对不会造成内存泄漏。

1.要删除事件监听器,您需要将函数作为参考传递,因此addEventListener('click',function(){alert('something')},false)不起作用。

2.我阅读了var that=this内部函数创建泄漏之类的引用,对吗?

已知方式

function class(a){
 this.name=a;
 var that=this;// reference
 //simply reference the whole object as a variable.

 var bindedClick=this.click.bind(this);//bind the click to the class
 //(js 1.85)
 //can i use removeEventListener('click',bindedClick,false) later?
 //apply && call (js 1.3)
}

由于我不确定是否var that=this(因为它是整个对象)创建泄漏,所以有时我通过将信息保存到数组中来最小化这种情况,并作为参考我使用一个 id..

var class={};
var id='ID'+Date.now();

class[id].info={here i store all the info i need later text only}
//this will be stored also in a cookie / Localstorage to reuse later.

class[id].dom={here i store the dom references}
class[id].events{here i store the xhr events}//if needed
//this are Temp only

并获取信息,我只是通过将 id 添加到事件元素来传递它

class[id].events.xhr.id=id;
class[id].events.xhr.onload=class.f

class.prototype.f=function(e){
 //this.response,class[this.id]<- access everything.
 this.removeEventListener('load',class.f,false);
 delete class[this.id].events.xhr;
 delete this.id
}

class[id].dom.id=id;
class[id].dom.onclick=class.f2

class.prototype.f2=function(e){
 //class[e.target.id],class[this.id]<- access everything.
 this.removeEventListener('click',class.f2,false);
 delete class[this.id].dom;
 delete this.id
}

正如您在上面的示例中看到的那样,我可以访问所有内容,并且引用只是一个小字符串...

我存储 dom 因为我在加载时定义了 dom 引用,所以我不必getElementById()每次都调用;

我将 XHR 之类的事件存储在类中,因为我希望能够从外部调用 xhr.abort() 并且还能够调用 removeEventListener。

我需要尽量减少对内存的影响,但另一方面我需要控制许多具有多个同时事件的元素,以通过删除所有事件和引用来“手动”控制垃圾收集器。

为了确保您了解问题比它看起来更大......:它是 chrome 的下载管理器。下载地址的输入框:

1.xhr to get the fileinfo (size,name,acceptranges,mime)
2.store info in localstorage and cached array
2.create visual dom elements to show progress (event:progress,click,mouseover..)
3.xhr request a chunk 0-1000(event:load)
4.onprogress display progress data (event:progress,error)
5.request filesystem(event:ok,error)
6.readfile/check file (event:ok,error)
7.request filewriter (event:ok,error)
8.append to file (events=ok,error) 
9.on ok start again from 3. until file is finished
10.when finished i delete all the **references / events / extra info**
//this to help the garbage collector.
11.change the dom contents.

每个文件每秒都有很多事件。

这 3 个中哪一个是最好的解决方案或有更好的解决方案?

bind();//or call / apply

var that=this; //reference to the whole object

var id=uniqueid; // reference to the object's id

根据答案:

(function(W){
 var D,dls=[];
 function init(){
  D=W.document;
  dls.push(new dl('url1'));
  dls.push(new dl('url2'));
 }
 function dl(a){
  this.MyUrl=a;
  var that=this;
  var btn=D.createElement('button');
  btn.addEventListener('click',this.clc,false);
  D.body.appendChild(btn);
 }
 dl.prototype={
  clc:function(e){
    console.log(that)
  }
 }
 W.addEventListener('load',init,false);
})(window)

var that=this 不起作用。

这行得通......但我需要很多检查,如果并执行多个功能。

(function(W){
 var D,dls=[];
 function init(){
  D=W.document;
  dls.push(new dl('url1'));
  dls.push(new dl('url2'));
 }
 function dl(a){
  this.MyUrl=a;
  this.btn=D.createElement('button');
  btn.addEventListener('click',this,false);
  D.body.appendChild(btn);
 }
 dl.prototype={
  handleEvent:function(e){
   e.target.removeEventListener('click',this,false);//does this the work?
   return this.clc(e);
  },
  clc:function(e){
   console.log(this,e)
  }
 }
 W.addEventListener('load',init,false);
})(window)

绑定:

(function(W){
 var D,dls=[];
 function init(){
  D=W.document;
  dls.push(new dl('url1'));
  dls.push(new dl('url2'));
 }
 function dl(a){
  this.MyUrl=a;
  this.clcB=this.clc.bind(this);
  this.btn=D.createElement('button');
  this.btn.addEventListener('click',this.clcB,false);
  D.body.appendChild(this.btn);
 }
 dl.prototype={
  clc:function(e){
   e.target.removeEventListener('click',this.clcB,false);//does this the work?
   delete this.clcB;
   console.log(this)
  }
 }
 W.addEventListener('load',init,false);
})(window)
4

2 回答 2

6

更好的解决方案是让您的“类”实现EventListener接口。

您可以通过handleEventMyClass.prototype. 这允许您将对象直接传递给.addEventListener()而不是传递处理程序。

当事件发生时,handleEvent()将调用该方法,并将您的对象作为this值。这使您可以访问对象的所有属性/方法。

function MyClass(a) {
    this.name = a;

    // pass the object instead of a function
    document.addEventListener('click', this, false);
    xhr.addEventListener('load', this, false); // where did `xhr` come from?

    this.normal()
}

MyClass.prototype = {

    // Implement the interface
    handleEvent: function(e) {
        // `this` is your object
        // verify that there's a handler for the event type, and invoke it
        return this[e.type] && this[e.type](e);
    },

    click: function (e) {
        // `this` is your object
    },
    load: function (e) {
        // `this` is your object
    },
    normal: function (e) {
        // `this` is your object
    }
}

请注意,我将您的xhr方法的名称更改为load. 这使得根据事件类型调用正确的方法变得更加容易。

然后当需要调用时.removeEventListener(),只需像往常一样从元素中执行它,但再次传递对象而不是处理程序。

于 2013-08-16T15:22:19.557 回答
1

我阅读了var that=this内部函数创建泄漏之类的参考资料

错误的。他们创建的引用在函数完成之前不会被垃圾收集,但这正是您想要的。这不是泄漏。

它可能会在无法处理循环引用但根本不关心这些的非常旧的浏览器(IE6)中导致问题。此外,通过调用removeEventListener您甚至可以破坏该循环引用,因此一切都很好。

我通过将信息保存到数组中来最小化这一点,并且引用只是一个小字符串......

不。参考是你的数组,如果你忘记了来自 id 的 id,class它很可能会造成泄漏。delete不要把这件事复杂化。

这 3 个中哪一个是最好的解决方案或有更好的解决方案?

var that=this; //reference to the whole object

标准方法。很好。

.bind();

可以比that变量更简洁,并且具有相同的引用布局(垃圾回收没有区别)。请注意,原生bind浏览器在旧浏览器上不可用,因此有些人不赞成这种方法。也可以,但可能需要垫片。

var id=uniqueid; // reference to the object's id

不要那样做。它太复杂了,你很容易犯错误,从而导致巨大的泄漏。

带有handleEvent方法的事件侦听器接口(由 @CrazyTrain 提供

非常优雅,但大多数人都不知道。低内存占用,因为不需要创建绑定的特权函数。对于只需要处理一种事件类型,但在支持具有相同侦听器实例的不同事件或不同目标时需要某种委托的类(并且可能变得比其他方法更复杂),这非常适用。相反:该handlerEvent方法是公开的,所有有权访问您的实例的东西都可以“触发”(欺骗)事件。


var that=this 不起作用。

你用错了。这种方法的重点是创建一个闭包,其中新函数可以访问该that变量。闭包范围是您的构造函数,您无法从原型访问它。

var that=this;
btn.addEventListener('click',function(e){that.clc(e);},false);

// use `this` in the prototype

handleEvent工作......但我需要很多检查,如果并执行多个功能。

不,您的实例只处理click按钮的,所以这种方法对您来说很好。您甚至可以将所有代码clc直接放入handleEvent.

于 2013-08-16T15:30:54.593 回答