JB06 JB06 - 10 days ago 4
Javascript Question

jQuery event callback confusion

I have this clock object:

var Clock = {
start: function () {
$('#btnScrollPause').show();
$('#btnScrollResume').hide();

advance();
this.interval = setInterval(function () {
advance();
}, 5000);
},

pause: function () {
$('#btnScrollPause').hide();
$('#btnScrollResume').show();
this.reset();
},

resume: function () {
if (!this.interval) {
this.start();
}
},

reset: function () {
clearInterval(this.interval);
delete this.interval;
}
};


I have two buttons that pause and resume automatic scrolling of an element, but my confusion is on the click handlers that I've attached to them.

If I call the pause function like this:

// 1
$('#btnScrollPause').click(function () {
Clock.pause();
});


It works correctly, however if I try to reduce the code like so:

// 2
$('#btnScrollPause').click(Clock.pause);


It no longer works and I get the error "this.reset is not defined". Also, if I make a function and use that in the click handler instead:

// 3
$('#btnScrollPause').click(scrollPause);

function scrollPause() {
Clock.pause();
}


It works! Can someone explain why 1 and 3 work, but 2 doesn't?

Answer

The this object binding is volatile in JavaScript...that is, it doesn't always point to the same object. How you invoke the code that contains the word this determines what object it will bind to.

Had you made an instance of Clock (i.e. var c = new Clock()) and then said:

$('#btnScrollPause').click(c.pause);

It would have worked because this would be bound to the instance of the Clock referenced by the c variable.

But as it stands with your code, this is bound to your button because that is the object that initiated the code.

Here's a checklist that you can follow to know what this will bind to...

If the code that contains this is invoked:

  1. As a method or property of an object instance (through an instance variable):

    var o = new Object(); 
    
    // "this" will be bound to the "o" object instance
    // while "someProperty" and "someMethod" code executes
    o.someProperty = someValue;
    o.someMethod();
    
  2. Via a .call(), .apply(), .bind() or Array.prototype.fn invocation:

    // "this" will be bound to the object suppled as the "thisObjectBinding"
    someFunction.call(thisObjectBinding, arg, arg);
    someFunction.apply(thisObjectBinding, [arg, arg]);
    var newFunc = someFunction.bind(thisObjectBinding, arg, arg);
    

    Additionally, several Array.prototype methods allow for a thisObject to be passed which will alter the binding for the duration of the method call:

    Array.prototype.every( callbackfn [ , thisArg ] )
    Array.prototype.some( callbackfn [ , thisArg ] )
    Array.prototype.forEach( callbackfn [ , thisArg ] )
    Array.prototype.map( callbackfn [ , thisArg ] )
    Array.prototype.filter( callbackfn [ , thisArg ] )
    
  3. If none of the other scenarios apply, Default binding occurs.

    3a. With "use strict" in effect: this is undefined

    3b. Without "use strict" in effect: this binds to the Global object

** NOTE: this binding can also be affected by using eval(), but as a general best practice, the use of eval() should be avoided.

Comments