admin管理员组文章数量:1391977
As we all know when we create a class in JavaScript a normal function returns the class object but events return the event object and the class object gets lost:
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
}
}
Whats the best way to bind those events to our class?
By best way I mean no or just a tiny reference, the ability to remove the events in a native way (removeEventListener
) and to absolutely not create memory leaks.
to remove an eventlistener you need to pass the function as a reference, so
addEventListener('click',function(){alert('something')},false)
does not work.I read references like
var that=this
inside functions create leaks; true?
Known ways:
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)
}
As I'm not sure if var that=this
(as it is the whole object) creates leaks, sometimes I minimize this by saving the info into an array and as reference I use a 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
and to get the info I just pass the id by adding it to the event element:
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
}
As you can see in this example above I have access to everything and the reference is just a small string.
I store the DOM because I define the DOM references on load so I don't have to call getElementById()
every time.
I store the events like XHR in the class as I want to be able to call xhr.abort()
from outside, and also able to call removeEventListener
.
I need to minimize the impact to the memory but at the other side I need to control many elements that have multiple simultaneous events to control the garbage collector "manually" by removing all events and references.
To make sure you understand that the problem is bigger than it looks: it's a download manager for Chrome. Input field for download URL:
xhr
to get the fileinfo (size, name, acceptranges, mime)- store info in localstorage and cached array
- create visual DOM elements to show progress (event:progress, click, mouseover..)
xhr
request a chunk 0-1000 (event:load)- onprogress display progress data (event:progress,error)
- request filesystem(event:ok,error)
- readfile/check file (event:ok,error)
- request filewriter (event:ok,error)
- append to file (events=ok,error)
- on ok start again from 3. until file is finished
- when finished I delete all the references / events / extra info //this to help the garbage collector.
- change the DOM contents.
Every file has sooo many events every sec.
Which of these 3 is the best solution or are there any better solutions?
bind();//or call / apply
var that=this; //reference to the whole object
var id=uniqueid; // reference to the object's id
BASED ON the answers:
(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
does not work.
This works; but I need a lot of checks, switch if and execute multiple functions.
(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)
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)
As we all know when we create a class in JavaScript a normal function returns the class object but events return the event object and the class object gets lost:
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
}
}
Whats the best way to bind those events to our class?
By best way I mean no or just a tiny reference, the ability to remove the events in a native way (removeEventListener
) and to absolutely not create memory leaks.
to remove an eventlistener you need to pass the function as a reference, so
addEventListener('click',function(){alert('something')},false)
does not work.I read references like
var that=this
inside functions create leaks; true?
Known ways:
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)
}
As I'm not sure if var that=this
(as it is the whole object) creates leaks, sometimes I minimize this by saving the info into an array and as reference I use a 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
and to get the info I just pass the id by adding it to the event element:
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
}
As you can see in this example above I have access to everything and the reference is just a small string.
I store the DOM because I define the DOM references on load so I don't have to call getElementById()
every time.
I store the events like XHR in the class as I want to be able to call xhr.abort()
from outside, and also able to call removeEventListener
.
I need to minimize the impact to the memory but at the other side I need to control many elements that have multiple simultaneous events to control the garbage collector "manually" by removing all events and references.
To make sure you understand that the problem is bigger than it looks: it's a download manager for Chrome. Input field for download URL:
xhr
to get the fileinfo (size, name, acceptranges, mime)- store info in localstorage and cached array
- create visual DOM elements to show progress (event:progress, click, mouseover..)
xhr
request a chunk 0-1000 (event:load)- onprogress display progress data (event:progress,error)
- request filesystem(event:ok,error)
- readfile/check file (event:ok,error)
- request filewriter (event:ok,error)
- append to file (events=ok,error)
- on ok start again from 3. until file is finished
- when finished I delete all the references / events / extra info //this to help the garbage collector.
- change the DOM contents.
Every file has sooo many events every sec.
Which of these 3 is the best solution or are there any better solutions?
bind();//or call / apply
var that=this; //reference to the whole object
var id=uniqueid; // reference to the object's id
BASED ON the answers:
(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
does not work.
This works; but I need a lot of checks, switch if and execute multiple functions.
(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)
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)
Share
Improve this question
edited Mar 22, 2023 at 18:19
Brian Tompsett - 汤莱恩
5,89372 gold badges61 silver badges133 bronze badges
asked Aug 16, 2013 at 15:06
coccococco
16.7k7 gold badges64 silver badges77 bronze badges
2 Answers
Reset to default 6Better solution is to have your "class" implement the EventListener interface.
You do this by adding a handleEvent
method to MyClass.prototype
. This allows you to pass the object directly to .addEventListener()
instead of passing a handler.
When an event occurs, the handleEvent()
method will be invoked, with your object as the this
value. This allows you to have access to all the properties/methods of the object.
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` e 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
}
}
Notice that I changed the name of your xhr
method to load
. This makes it easier to call the proper method based on the event type.
Then when it es time to call .removeEventListener()
, just do it like normal from the element, but again pass the object instead of the handler.
I read references like
var that=this
inside functions create leaks
Wrong. They create references that are not garbage-collected until the function is, but that's exactly what you want. It's not a leak.
It might cause problems in very old browsers (IE6) that cannot handle cyclic references, but simply don't care about those. Also, by calling removeEventListener
you even destroy that cyclic reference so everything is fine.
I minimize this by saving the info into an array and the reference is just a small string...
No. The reference is your class
array which will much more likely create a leak if you forget to delete
the ids from id. Don't overplicate this.
Which of these 3 is the best solution or are there any better solutions?
var that=this; //reference to the whole object
The standard approach. Very fine.
.bind();
Can be more concise than a that
variable, and has the same reference layout (no difference in garbage collection). Notice that a native bind
is not available on old browsers, so some people frown upon this method. Fine as well, but might need a shim.
var id=uniqueid; // reference to the object's id
Don't do that. It's too plicated, and you easily make mistakes that really lead to huge leaks.
The event listener interface with a
handleEvent
method (presented by @CrazyTrain)
Very elegant, but unknown to the most people. Low memory footprint since no bound, privileged functions need to be created. Works very well for classes that need to handle only one event type, but needs some kind of delegation when supporting different events or different targets with the same listener instance (and can bee more plex than the other approaches). Contra: The handlerEvent
method is public and everything that has access to your instance can "fire" (spoof) events.
var that=this does not work.
You're using it wrong. The point of this approach is to create a closure in which new functions have access to the that
variable. The closure scope is your constructor, you cannot access it from the prototype.
var that=this;
btn.addEventListener('click',function(e){that.clc(e);},false);
// use `this` in the prototype
handleEvent
works... but i need alot of checks , swich if and execute multiple functions.
No. Your instances do only handle the click
of the button, so this approach is fine for you. You even could put all of your code from clc
directly into handleEvent
.
本文标签:
版权声明:本文标题:javascript - Bind an event function to a class but using removeEventListener and removing the references allowing the garbagecol 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1744725047a2621912.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论