Question

So, this is probably answered somewhere on this site, but I can't find it, if it is.

I'm having trouble figuring out why one of my this references inside functions seems to be resolved when I create the object, and one when I call the function that has the reference inside it. Here's some code:

function MyObj (name) {
    this.locked = false;
    this.name = name;
    this.elem = null;
    this.func1 = function () {
        if (this.locked) return;
        /* code that changes this.name here */
        this.elem.innerHTML = this.name;
    };
    this.func2 = function () {
        this.locked = !this.locked;
        if (this.locked) this.elem.className = "locked";
        else this.elem.className = "unlocked";
    };
}

var myObjGlobal = new MyObj("foo");

function callFunc1 () {
    myObjGlobal.func1();
}

Then I have a function that is called on document load:

function onLoad() {
    var myElem = document.getElementById("myElem");
    myObjGlobal.elem = myElem;
    myElem.onclick = myObjGlobal.func2;
    document.getElementById("myButton").onclick = callFunc1;
}

I've made sure all my html elements have the right ids. When I click myButton, I get no errors. However, when I click myElem, I get Uncaught TypeError: Cannot set property 'className' of undefined.

Why is the first this set when I call the function, and the second this set when I create the object? (Or so it seems?)

here's a working jsfiddle showing the problem (with the given example code).

Thanks in advance!

Was it helpful?

Solution

myElem.onclick = myObjGlobal.func2;

This doesn't do what you think inn JavaScript. It doesn't give you func2 with the object "attached" to it in any way; it just gives you func2. When it gets called later, it's called as a method of myElem, so that's what this is.

This is a gigantic and awful wart in JS. :)

You can either wrap it in another function:

myElem.onclick = function() {
    myObjGlobal.func2();
};

Or use .bind, which does effectively the same thing, and which is supported almost universally nowadays:

myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);

Note also that assigning to onclick is a little rude, since you'll clobber any existing click handler. You may want addEventListener instead.

OTHER TIPS

myElem.onclick = myObjGlobal.func2;

This loses myObjGlobal entirely; myObjGlobal.func2 is just a function, with nothing tying its this to anything. In JavaScript, the this of a function is determined when it’s called, not when it’s defined. This is a fantastic and useful feature of JavaScript that’s much more intuitive than, say, Python. When myElem.onclick is called, it’ll be called with this bound to myElem.

Function.prototype.bind is a utility to do what you’re doing with callFunc1, by the way:

myElem.onclick = myObjGlobal.func2.bind(myObjGlobal);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top