19

I have some javascript code (within an object) :

toggle: function() {
    var me = this;
    var handler = function() { me.progress() };
    me.intervalId = setInterval(handler, me.intervalTime);
    //...More code
}

I'm kind of new to javascript, so doing the above as far as I can tell actually passes the me variable into anonymous the function. I was wanting to see if there is a more declarative way to do so? I wanted something along the line of:

var handler = (function(o) { o.progress();})(this));

but that doesn't seem to be working... Am I missing something? Is this a case where "this is the way the language works so just declare a local variable and deal with it"?

UPDATE:

The source to my problem was/is my unclear understanding of scope and closures in javascript. I found this article to help me understand a little more.

Jose
  • 10,891
  • 19
  • 67
  • 89
  • 1
    It's not working because you have one `)` too many. Doesn't `var handler = (function(o) { o.progress();})(this);` work perhaps? – pimvdb May 31 '11 at 14:12

5 Answers5

30

You can use ".bind()":

var handler = function() { this.progress(); }.bind(this);

New browsers have "bind()", and the Mozilla docs have a solid implementation you can use to patch older browsers.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • 3
    an even more terse solution for my situation is `var handler = this.progress.bind(this)`. Thanks for your input! – Jose Jun 13 '11 at 13:12
6

The reason

var handler = (function(o) { o.progress();})(this));

doesn't work because it just immediately calls the anon function, therefore immediately calling o.progress() and assigns the return value of the anon function (undefined) to handler. You need to return an actual function from the outer function:

handler = (function(me){
    return function(){
        return me.progress();
    }
}(this));

On the flip side this is equivalent and just as bad looking as bad looking as the variable assignment (but can still be useful, particularly if this needs to be done in a loop, with the changing i rather than the fixed this).


BTW, if the progress function doesn't have any calls to this inside it , just doing handler = this.progress (without the parens) might suffice.

hugomg
  • 68,213
  • 24
  • 160
  • 246
5

The anonymous function has access to me because it is declared inside of the outer function (the toggle function); it is closed over by the outer function.

Your handler function will be called by setInterval, which passes exactly zero arguments. This means you can't use parameters in the handler function itself.

I you really want to pass me explicitly, you could write a function accepting an parameter, and have that function return an anonymous function without parameters, but which could access the creator function's parameter:

toggle: function() {
    var me = this;
    var handler = (function (o) { return function() { o.progress() }; })(me);
    me.intervalId = setInterval(handler, me.intervalTime);
    //...More code
}

But this basically adds a layer of redirection without really making it more legible. Unless you pull that creating function outside:

function createProgressHandler(o) {
    return function() {
        o.progress();
    };
}

// ...

toggle: function() {
    var me = this;
    var handler = createProgressHandler(me);
    me.intervalId = setInterval(handler, me.intervalTime);
    //...More code
}
Martijn
  • 13,225
  • 3
  • 48
  • 58
1

What you have there is a closure. The function that is created and assigned to handler keeps a reference to the me object. This is normal, everyday JavaScript, and that's the way that closures work generally.

Adam Crossland
  • 14,198
  • 3
  • 44
  • 54
  • 1
    Thats not his problem. He is complaining that the `this` can't be closed over, forcing him to `var me = this`. – hugomg May 31 '11 at 14:31
  • I agree, but what he wants to do amounts to fighting the language. I -- and probably everyone else who writes JavaScript code -- would like to do without the local variable assignment, but it is what it is. – Adam Crossland May 31 '11 at 15:17
0

Have you tried to return the function like this?

var handler = function(o){
   return function(){
      o.progress();
   }
}(me);

Now you can call:

handler();
Thomas
  • 463
  • 3
  • 8