Heres a bit of cleaned up code from a picture gallery im writing to learn js.
I create the gallery as an object but at some point i lose track of what "this" points to.
It doesnt make sense to me what happens at the bottom (look at the comments).
Can someone please explain?
function Gallery(parentID)
{
// ...
this.showDiv = document.createElement("div");
// ...
this.show = function ()
{
document.body.appendChild(this.showDiv); //will be given css absolute position to "pop up"
this.showDiv.innerHTML = '<img class="displayImage" src="' + this.picList[this.showIndex] + '">'; //fill with image
this.showDiv.focus();
this.showDiv.onclick = this.hide;
}
this.hide = function ()
{
alert(this.innerHTML); // <= WHY DOES THIS SHOW THE INNERHTML CONTENTS OF this.showDiv??????
//alert(this.showDiv.innerHTML); // <= shouldnt this be correct?
this.parentNode.removeChild(this); //doesnt work
}
}
Let me know if i cleaned up some code that might have affected the results and ill fill it in.
Thanks.
this.showDiv.onclick = this.hide;
This line is the problem. It doesn’t work like in Python; this.hide
is not a bound method. It’s just the hide
function, not bound to any particular this
.
One way to fix it is:
this.showDiv.onclick = this.hide.bind(this);
But the .bind()
method of functions is a rather new standard; it isn’t in all old browsers. So you might want to roll your own instead:
function bindMethod(object, func) {
return function () { return func.apply(object, arguments); };
}
this.showDiv.onclick = bindMethod(this, this.hide);
(Generally this
is kind of strangely behaved in JavaScript; the main thing to remember is that this
always refers to the innermost function’s this
, which could be whatever the caller wants it to be. Sometimes you see JS code do things like var self = this;
to give a particular function’s this
a name that can be used in nested functions; in Python, of course, that is just sort of the way things work automatically.)
This refers to the object that called the function. So when you say this.showDiv.onclick
, showDiv is what is calling the function. Therefore this.innerHTML
actually means this.showDiv.innerHTML
If you insist on using a constructor function, rather than a factory, then keep this
in mind --
If you have a method (or any function you call as a method, and not as a constructor), and that method references this
, that this
statement gets resolved at the instant the function is called rather than when it is defined.
function sayName () { console.log(this.name); }
var bob = { name : "Bob" },
doug = { name : "Doug" };
bob.greet = sayName;
doug.greet = sayName;
In a strict class-based language, there'd be all kinds of errors, here.
But through the magic of late-binding (deciding what this
means at the last possible second, during execution), it goes off without a hitch.
There is one hitch, however:
Functions inside of functions.
You want this
to point to the this
of the outer method.
It doesn't.
In old (current) JS, it points at window
.
In future JS, it points at nothing.
function sayNameAndAge () {
var outerThis = this,
name = this.name;
function sayAge () { console.log(outerThis.age); }
console.log(name);
sayAge();
}
Assuming Bob has both a name and an age, now, you can assign the function like last time, and it will work.
The function will see that bob
was in front of the dot, and thus the subject of this
(note: not really how it works, but how it appears to work, one function deep). outerThis
will save the reference for the inner function to use.
Another option is:
sayNameAndAge.call(bob);
call
is a method that functions have (did I mention that functions are objects), which lets you specify this
for that particular execution, rather than giving another object a method (a reference, really).
bind
works just like call
, except it returns a function where the this
is permanently set to value you passed in. And bind
isn't in OldIE.
One other major point of reference:
When you use event-listeners, this
points to the element you put the listener on.
You can delegate listeners (attaching one listener to a <ul>
, that listens for any click on any of its <li>
, for example.
In that instance, this
would be the <ul>
, because it would be the object which the listener was attached to, when it was triggered.
Hope that makes things a little clearer.