开发者

Having the correct value of 'this' in JS

I have two Javascript "objects" similar to so....

var Object2 = new (function() {
    this.FetchData = function(callback) {
        // do some stuff
        callback(data);
    };
});

var Object1 = new (function() {

    this.DisplayStuff = function() {

    };

    this.LoadData = function() {
        Object2.FetchData(this.OnData);
    };

    this.OnData = function(data) {
        // this == window
        this.DisplayStuff();   // doesn't w开发者_如何学Pythonork
    };

});

When Object1 receives the callback to OnData, the value of "this" is set to window. Is there any way I can get around this so that the value of "this" inside of OnData will be the instance of Object1 instead of window?


A technique commonly used in frameworks is to let the caller decide on what the this context should be for the callback function. So the FetchData function looks like (thanks to @Andy for telling me about the default scope when context is null or undefined - it is the global object),

this.FetchData = function(callback, context) {
    callback.call(context, data);
};

When calling FetchData, pass Object1 as the context for the callback,

Object2.FetchData(this.OnData, this);

Another option is to bind the OnData function with Object1 using Function.prototype.bind

Object2.FetchData(this.onData.bind(this));


The simple way of doing it is storing a reference to this in a variable, then using the call() method:

this.LoadData = function() {
    var self = this;
    Object2.FetchData(function () { self.OnData.call(self) });
};

If you're doing this a lot, you might want to consider using the .bind() method on the function prototype in ECMAScript 5th edition. This method can be implemented where unsupported:

// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
  Function.prototype.bind = function(){ 
    var fn = this, args = Array.prototype.slice.call(arguments),
        object = args.shift(); 
    return function(){ 
      return fn.apply(object, 
        args.concat(Array.prototype.slice.call(arguments))); 
    }; 
  };
}

And the resulting function call:

this.LoadData = function() {
    Object2.FetchData(this.OnData.bind(this));
};

PrototypeJS - bind()


The basic solution is to force context on the callback with apply. (Or call. The difference is how parameters are passed. Either an array or 'normally') See the MDC for documentation.


For a callback, you're stuck with 'this' being the window (usually). However, you could add a member to Object1 that points to itself (a lot of times it's gets call 'self', i.e. "var self = this;"), and then use that, i.e. self.DisplayStuff().


The ambiguity of "this" is really annoying to me with javascript. So I just avoid "this" as much as possible. I do it this way:

var Object2 = (function() { // no need for a "new" keyword any more

    var self = {}; // instead of the auto-supplied "this", create my own "self"

    self.FetchData = function(callback) {
        // do some stuff
        callback(data);
    };
    return self; // don't forget to return "self"
});

var Object1 = (function() {

    var self = {}; // instead of the auto-supplied "this", create my own "self"

    self.DisplayStuff = function() {

    };

    self.LoadData = function() {
        Object2.FetchData(this.OnData);
    };

    self.OnData = function(data) {
        self.DisplayStuff();
    };

    return self; // don't forget to return "self"

});

You lose the ability to use Object.prototype with this technique, you lose the ability to test using "instanceof", but you never have to think about what "this" is.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜