6

Something has always bothered me about the way I do object-oriented coding in Javascript. When there's a callback, I frequently want to reference the object which originally called the function, which leads me to do something like this:

MyClass.prototype.doSomething = function(obj, callback) {
    var me = this; // ugh
    obj.loadSomething(function(err, result) {
        me.data = result; // ugh
        callback(null, me);
    });
}

First off, creating the additional variable alway seemed... excessive to me. Furthermore, I have to wonder if it might end up causing problems (circular references? un-GCd objects?) by passing the "me" variable back to the callback.

Is there a better way to go about this? Is this approach evil?

6
  • 1
    this is not a variable. this cannot be closed over. The approach is fine.
    – user166390
    Commented Oct 31, 2012 at 19:16
  • 1
    The FUD about "evil" and "excessive" bothers me. It's okay to not like the boilerplate but why invent imaginary problems with it? You need to retain a reference to the same enclosing data no matter what approach you use, because the inner function doesn't change. Whether you'll only keep a reference to the least data necessary is something that the interpreter should worry about.
    – millimoose
    Commented Oct 31, 2012 at 19:17
  • @millimoose Well said - I failed to come up with a way to summarize that last line. (And I still run into "evil" - well not "evil", but truly awful in a very objective way - code that uses new Function("..") to "avoid issues".)
    – user166390
    Commented Oct 31, 2012 at 19:18
  • @millimoose perhaps you misunderstood. I was uncertain that this was the correct boilerplate to begin with. It was a practice I had stolen from somewhere along the line and never confirmed that it was, indeed, a good practice... thus the FUD.
    – Zane Claes
    Commented Oct 31, 2012 at 19:22
  • 1
    @pst Hell, if we define "evil" as "makes maintaining more difficult", it's probably less evil to bind this to a meaningful name instead of having the same "variable" refer to a bunch of different objects over the course of a single logical function. (Where, given the nature of JS code, a "logical function" can contain quite a few other tiny ones, like jQuery.each() callbacks, event handlers, or continuations.)
    – millimoose
    Commented Oct 31, 2012 at 19:22

5 Answers 5

8

This is what Function.bind() is for:

MyClass.prototype.doSomething = function(obj, callback) {
    obj.loadSomething((function(err, result) {
        this.data = result;
        callback(null, this);
    }).bind(this));
}
2
  • 3
    Might want to change me to this
    – Shmiddty
    Commented Oct 31, 2012 at 19:41
  • +1 for the good answer, accepted answer for the documentation link.
    – Zane Claes
    Commented Oct 31, 2012 at 20:34
7

AFAIK, what you are doing is the accepted pattern for this kind of thing, and doesn't cause any issues. A lot of people use either "self" or "that" as the stored reference - "self" can be more intuitive if you come from a python background.

2
  • 2
    I like self. However there is also window.self .. although I have never run into confusion on the issue.
    – user166390
    Commented Oct 31, 2012 at 19:16
  • And self can be confused sometimes with window.self Commented Oct 31, 2012 at 19:17
3

This is normal behavior for JavaScript. The context for this has changed for the loadSomething object, and the reason for having the callback is to capture closure references like your me variable.

3

You can bind this to the scope of the inner function

MyClass.prototype.doSomething = function(obj, callback) {
    obj.loadSomething(function(err, result) {
        this.data = result;
        callback(null, this);
    }.bind( this ));
}
1

This is the most common way I have seen to handle it, I usually use var self = this;, but its just a name. Even though it is an extra variable, considering the javascript overhead and the fact that it doesn't duplicate the object, it really isn't going to impact performance.

Not the answer you're looking for? Browse other questions tagged or ask your own question.