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.
精彩评论