0

I am a little bit confused on how this works, let me post you an example code:

someClass = function() {
    this.a = 10;

    this.Foo = function() {
        console.log(this.a); // will output "10"
        setTimeout(this.Bar, 1);
    }

    this.Bar = function() {
        console.log(this.a); // undefined
    }
}
var instance = new someClass();
instance.Foo();

My understanding is that this.a is not visible in function Bar if it is called from setTimeout (or some other "handler" type of thing.) What is the common/correct way of solving that?

(I am trying this in Node.js btw)

Thank you.

Avetis Zakharyan
  • 847
  • 2
  • 7
  • 19
  • `this` is confusing in javascript. When you call from the `setTimeout` function the context gets switched. See the thread [here](http://stackoverflow.com/questions/3127429/javascript-this-keyword) for help – Matt Dodge Apr 10 '13 at 19:31
  • Thank you, I'll do some reading! :) – Avetis Zakharyan Apr 10 '13 at 19:54

2 Answers2

2

When passing the function this.Bar as an argument to another function, you need to bind this.Bar to the context you'd like it to be executed with.

If you're using a JS library like jQuery or Underscore.js they already come with that functionality:

setTimeout(_.bind(this.Bar, this), 1);

Here's a simple implementation of a bind function:

var bind = function(scope, fn) {
  return function() {
    return fn.apply(scope, arguments);
  };
}

Update:

As @generalhenry pointed out, node.js already comes with a bind function in (Function.prototype.bind), so you can do this without adding a custom bind function nor an external library:

setTimeout(this.Bar.bind(this), 1);
Felipe Brahm
  • 3,113
  • 1
  • 25
  • 42
  • Node.js has Function.prototype.bind, why confuse things with a library or custom implimentation? – generalhenry Apr 10 '13 at 19:50
  • @generalhenry: my answer was a general JS answer.. I mentioned jQuery and Underscore.js as examples, but if node.js already comes with it, then of course you should use that ;) – Felipe Brahm Apr 10 '13 at 23:01
2

The problem is that the scope (this) is lost when you pass a function to setTimeout.

Here's the easiest way to fix it, store this as a reference through a closure and use that instead.

// This uses _me_ everywhere for consistency, but the only place that really needs it 
// is within the Bar method. But consistency in this case makes your code more change-
// proof, say if someone calls setTimeout(instance.Foo, 1)
someClass = function() {

    var me = this;
    me.a = 10;

    me.Foo = function() {
        console.log(me.a); // will output "10"
        // setTimeout will cause me.Bar to be called with window as the context
        setTimeout(me.Bar, 1);
    }

    me.Bar = function() {
        // so we avoid using _this_ in here
        console.log(me.a); // 10
    }
}

A slightly more elegant way is to use Function.bind

someClass = function() {
    this.a = 10;

    this.Foo = function() {
        console.log(this.a); // will output "10"
        // the bind call will force it to use the first argument as `this`
        setTimeout(this.Bar.bind(this), 1);
    }

    this.Bar = function() {
        console.log(this.a); // undefined
    }
}
Juan Mendes
  • 80,964
  • 26
  • 138
  • 189
  • I understand you're using `me` everywhere for consistency, but it's worth noting that, in this example, the only place where it's strictly necessary is inde `Bar`. – bfavaretto Apr 10 '13 at 19:40
  • @bfavaretto Yes, I do tend to choose between one or the other for clarity/consistency, what if you end up calling one of the other functions on a timeout? Or assign them to a different object? – Juan Mendes Apr 10 '13 at 19:42