I am new to OOP Javascript and am having trouble with the this
keyword and events.
What I'm trying to achieve is: I have multiple DOM objects and want to not only bind a mon event to them, but keep some data about the aforementioned objects in a global container (to increase runtime performance).
So what I do is basically this:
function ClassThatDoesSomething() {
/* keeps node ids for processing in this.init */
this.nodes = new Array();
/* keeps processed node data for fast access */
this.nodeData = new Array();
this.sthAddNodes = function(/* ids */) {
/* appends node ids to local variable (this.nodeData) */
}
function init() {
/* gathers data from all nodes that were
added before and stores it in this.nodeData */
/* here, unsurprisingly, 'this' references the window element*/
addEvent(window,'scroll',this.scroll);
}
function scroll() {
/* do stuff when user scrolls the page */
/* 'this' references the window element here too */
}
addEvent(window,'load',this.init);
}
Later, in the document body, I could just add this:
var Ctds = new ClassThatDoesSomething();
And further on, add DOM elements by:
Ctds.addNodes(ids);
No further implementation code would be required.
QUESTION: How to access the JS class instance in the init
and scroll
methods and not the window element.
It doesn't have to be through the this
keyword, I know, but still I didn't e up with anything.
P.S.
addEvent
is an extremely basic function to attach events, it's just IE/Fx friendly and does nothing else.- The code I'm writing is already functional, but in procedural form, I just wanted to OOP'd it.
- As a minor sub-question, I got the impression somehow, that getter/setter methods are discouraged in javascript, is it okay if I use them?
I am new to OOP Javascript and am having trouble with the this
keyword and events.
What I'm trying to achieve is: I have multiple DOM objects and want to not only bind a mon event to them, but keep some data about the aforementioned objects in a global container (to increase runtime performance).
So what I do is basically this:
function ClassThatDoesSomething() {
/* keeps node ids for processing in this.init */
this.nodes = new Array();
/* keeps processed node data for fast access */
this.nodeData = new Array();
this.sthAddNodes = function(/* ids */) {
/* appends node ids to local variable (this.nodeData) */
}
function init() {
/* gathers data from all nodes that were
added before and stores it in this.nodeData */
/* here, unsurprisingly, 'this' references the window element*/
addEvent(window,'scroll',this.scroll);
}
function scroll() {
/* do stuff when user scrolls the page */
/* 'this' references the window element here too */
}
addEvent(window,'load',this.init);
}
Later, in the document body, I could just add this:
var Ctds = new ClassThatDoesSomething();
And further on, add DOM elements by:
Ctds.addNodes(ids);
No further implementation code would be required.
QUESTION: How to access the JS class instance in the init
and scroll
methods and not the window element.
It doesn't have to be through the this
keyword, I know, but still I didn't e up with anything.
P.S.
addEvent
is an extremely basic function to attach events, it's just IE/Fx friendly and does nothing else.- The code I'm writing is already functional, but in procedural form, I just wanted to OOP'd it.
- As a minor sub-question, I got the impression somehow, that getter/setter methods are discouraged in javascript, is it okay if I use them?
7 Answers
Reset to default 10One thing I notice is that neither init
nor scroll
is a method on the instance.
So you only need to add init
and not this.init
to the load event:
addEvent(window,'load',init); // No "this." needed
And similarly:
addEvent(window,'scroll',scroll);
If you do decide to move them to the class (eg this.scroll
and this.init
etc), you can save a reference to this
and refer to it in an anonymous function passed to addEvent
:
var self = this;
this.init = function() {
addEvent(window, 'scroll', function() {
self.scroll()
})
};
this.scroll = function() { /* ... */ };
addEvent(window,'load',function() {
self.init()
});
This is called a closure.
function MyConstructor() {
this.foo = "bar";
var me = this;
function myClosure() {
doSomethingWith(me.foo);
}
}
this
is not determined until execution of the function. When attaching an event listener, you are passing a function, which does not carry scope with it. Therefore, on the specified event, the function is running in the scope of window
, meaning that this
will be equal to window
. To force execution in a particular scope, you can use tricks like creating a new variable to equal this
, such as:
var that = this;
...
addEvent(window,'scroll', function() {
that.scroll()
});
Add a method to the Function prototype that allows you to bind any function to any object:
Function.prototype.bind = function(object) {
var __method = this;
return function() {
return __method.apply(object, arguments);
};
};
Declare your event handlers in the instance (keeps things tidy):
function ClassThatDoesSomething() {
this.events = {
init: ClassThatDoesSomething.init.bind(this),
scroll: ClassThatDoesSomething.scroll.bind(this),
etc: ClassThatDoesSomething.etc.bind(this)
};
...
}
Now whenever you reference your events, they'll be automatically bound to the class instance. e.g.:
function init() {
addEvent(window,'scroll',ClassThatDoesSomething.events.scroll);
}
You can use closures for that:
function ClassThatDoesSomething() {
var self=this;
// ...
function init() {
addEvent(window,'scroll',self.scroll);
}
}
Do this:
var ClassThatDoesSomething = function() {
/* keeps node ids for processing in this.init */
this.nodes = new Array();
/* keeps processed node data for fast access */
this.nodeData = new Array();
}
ClassThatDoesSomething.prototype.sthAddNodes = function(/* ids */) {
/* appends node ids to local variable (this.nodeData) */
}
}
ClassThatDoesSomething.prototype.init = function() {
/* gathers data from all nodes that were
added before and stores it in this.nodeData */
/* here, unsurprisingly, 'this' references the window element*/
addEvent(window,'scroll',this.scroll);
}
}
ClassThatDoesSomething.prototype.scroll = function() {
/* do stuff when user scrolls the page */
/* 'this' references the window element here too */
}
addEvent(window,'load',this.init);
}
This trick should work:
function ClassThatDoesSomething() {
...
this.This = this;
...
}
Then inside those problematic methods you can use 'This'
.
Hope this helps.