将事件函数绑定到类,但使用removeEventListener并删除其引用,以允许garbagecollector正常工作

coc*_*cco 3 javascript javascript-events

众所周知,当我们在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
 }
}
Run Code Online (Sandbox Code Playgroud)

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

通过最好的方式,我的意思是没有或只是一个小参考,能够以本机方式删除事件(removeEventListener),绝对不会创建内存泄漏.

1.删​​除一个eventlistener你需要传递该函数作为参考,所以addEventListener('click',function(){alert('something')},false)不起作用.

2.i阅读引用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)
}
Run Code Online (Sandbox Code Playgroud)

因为我不是shure如果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
Run Code Online (Sandbox Code Playgroud)

并获取信息我只是通过将其添加到event元素来传递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
}
Run Code Online (Sandbox Code Playgroud)

正如您在上面的示例中所看到的,我对所有内容都有所了解,而且引用只是一个小字符串......

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

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

我需要最小化对内存的影响,但在另一方面,我需要控制许多具有多个同时事件的元素,通过删除所有事件和引用来"手动"控制垃圾收集器.

为了确保您了解问题更大,它看起来......:它是Chrome的下载管理器.下载URL的输入字段:

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.
Run Code Online (Sandbox Code Playgroud)

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

这三种中哪一种是最佳解决方案还是有更好的解决方案?

bind();//or call / apply

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

var id=uniqueid; // reference to the object's id
Run Code Online (Sandbox Code Playgroud)

根据答案:

(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)
Run Code Online (Sandbox Code Playgroud)

var that =这不起作用.

这工作...但我需要很多检查,如果和执行多个功能swich.

(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)
Run Code Online (Sandbox Code Playgroud)

BIND:

(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)
Run Code Online (Sandbox Code Playgroud)

小智 6

更好的解决方案是让您的"类"实现EventListener接口.

您可以通过添加handleEvent方法来执行此操作MyClass.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
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我将xhr方法的名称更改为load.这样可以更轻松地根据事件类型调用正确的方法.

然后,当需要调用时.removeEventListener(),只需从元素中执行正常操作,但再次传递对象而不是处理程序.

  • @cocco:您可以通过`e.currentTarget`引用该元素,或者只是将它作为对象的属性...只要您清理该元素是否从DOM中删除. (2认同)