42

I find myself assigning "this" to a variable so I can easily use it in callbacks and closures.

Is this bad practice? Is there a better way of referring back to the original function?

Here is a typical example.

User.prototype.edit = function(req, res) {

  var self = this,
      db = this.app.db;

  db.User.findById('ABCD', function(err, user)) {

    // I cannot use this.foo(user)
    self.foo(user);
  });
};

User.prototype.foo = function(user) {

};

Do you normally use this approach or have you found a cleaner solution?

4
  • 4
    That's the way to do it. Commented Oct 24, 2011 at 11:14
  • how about a solution like call(), apply() or even the ES5's bind() ?? Commented Oct 24, 2011 at 11:17
  • @Couto, call and apply still need a reference to this which brings you back to the original problem. bind is indeed a good solution. Commented Oct 24, 2011 at 11:20
  • @davin you're correct indeed, i did not remember that when i wrote the comment. Personally i use bind() Commented Oct 24, 2011 at 11:24

3 Answers 3

86

There are three main ways to deal with this in callbacks:

1. Create a lexically-scoped variable, as you are currently doing

The two most common names for this new variable are that and self. I personally prefer using that because browsers have a global window property called self and my linter complains if I shadow it.

function edit(req, res) {
    var that = this,
    db.User.findById('ABCD', function(err, user){
        that.foo(user);
    });
};

One advantage of this approach is that once the code is converted to using that you can add as many inner callbacks as you want and they will all seamlessly work due to lexical scoping. Another advantage is that its very simple and will work even on ancient browsers.

2. Use the .bind() method.

Javascript functions have a .bind() method that lets you create a version of them that has a fixed this.

function edit(req, res) {
    db.User.findById('ABCD', (function(err, user){
        this.foo(user);
    }).bind(this));
};

When it comes to handling this, the bind method is specially useful for one-of callbacks where having to add a wrapper function would be more verbose:

setTimeout(this.someMethod.bind(this), 500);

var that = this;
setTimeout(function(){ that.doSomething() }, 500);

The main disadvantage of bind is that if you have nested callbacks then you also need to call bind on them. Additionally, IE <= 8 and some other old browsers, don't natively implement the bind method so you might need to use some sort of shimming library if you still have to support them.

3. If you need more fine-grained control of function scope or arguments, fall back to .call() and .apply()

The more primitive ways to control function parameters in Javascript, including the this, are the .call() and .apply() methods. They let you call a function with whatever object as their this and whatever values as its parameters. apply is specially useful for implementing variadic functions, since it receives the argument list as an array.

For example, here is a version of bind that receives the method to bind as a string. This lets us write down the this only once instead of twice.

function myBind(obj, funcname){
     return function(/**/){
         return obj[funcname].apply(obj, arguments);
     };
}

setTimeout(myBind(this, 'someMethod'), 500);
Sign up to request clarification or add additional context in comments.

5 Comments

Some really great answers posted, this one having the best overview. I think I'll stick with my variable assignment, it's simple and easy to understand.
Interestingly, I just ran a (very simple) benchmark in nodejs, and method 1 was 100x faster than method 2.
Ran a similar benchmark. I didn't get 100x faster, but still significantly faster to use the closure method.
This should be updated with the new lambdas which are great for using the parent "this" context
@NeeL: Feel free to edit the answer with that extra information. My JS is a tad rusty.
2

Unfortunately this is the well-established way to do this, although that is a widespread naming convention for this "copy".

You can also try:

db.User.findById('ABCD', this.foo.bind(this));

But this approach requires foo() to have exactly the same signature as the one expected by findById() (in your example you are skipping err).

1 Comment

We've also chosen this approach at my work, partly because of this, and partly because using that has lower mental overhead to "parse" and understand.
2

You could create a proxy for the callback with:

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

Using this, you could do the following:

db.User.findById('ABCD', createProxy(function(err, user)) {
  this.foo(user);
}, this));

jQuery does something similar with: $.proxy

And, as others have noted using bind, have a look here if compatibility is an issue:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind#Compatibility

2 Comments

The question is tagged node.js, there aren't compatibility issues; bind works and should be preferred.
@davin Ah, missed that. But I guess this might be helpful outside of node.js too, so I'll leave it as it is.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.