Reputation: 26360
Now that's a new one for me. I searched around but can't find the solution elsewhere.
I've got this code (coffeescript) :
PositionDetector = ->
detectPosition : ->
console.log this # outputs 'Object' (OK)
navigator.geolocation.getCurrentPosition(this.locationHandler)
locationHandler : (position) ->
console.log this # outputs 'Window' (WHY??)
positionDetector = new PositionDetector()
positionDetector.detectPosition()
(or the corresponding compiled javascript, if you prefer) :
var PositionDetector = function() {
detectPosition : function() {
console.log(this); // outputs 'Object'
navigator.geolocation.getCurrentPosition(this.locationHandler);
},
locationHandler : function(position) {
console.log(this); // outputs 'Window'
}
}
var positionDetector = new PositionDetector();
positionDetector.detectPosition();
Question is, why does the first 'this' output 'Object' and the second one 'Window'?
Upvotes: 1
Views: 68
Reputation: 2210
Both T.J. Crowder & elclanrs are right.
As a brief explanation: "this" refers to object context (A.K.A. scope) and it does not work exactly as you expected in javascript unfortunately ,
IMHO this situation renders Javascript stupid. There are two methods to overcome this effect. First is the "bind" method as they have shown. The second one is the magical "closure" generation.
Upvotes: 0
Reputation: 1074218
Question is, why does the first 'this' output 'Object' and the second one 'Window'?
this
is set primarily by how a function is called (for now*), not where it's defined. In the first case, the way you're calling the function is through an object reference (positionDetector.detectPosition()
), and so the JavaScript engine sets this
to be the object you used as part of the expression making the call. In the second case, the function isn't being called as part of an expression retrieving the function reference from an object property, so this
takes its default value (the global object in loose mode, undefined
in strict mode). Here's a simpler example:
var obj = {
foo: function() {
console.log(this);
}
};
var f = obj.foo;
obj.foo(); // `this` is `obj` during the call
f(); // `this` is not `obj` during the call
To solve it, you could use Function#bind
to use a specific this
value during the callback:
navigator.geolocation.getCurrentPosition(this.locationHandler.bind(this))
Function#bind
creates a function that, when called, will call the original file with a specific this
value (and any optional arguments you provide).
More about this
(on my blog):
* "for now": ES6 introduces CoffeeScript-style arrow functions, which have a this
value that's set by the context in which the function is created. That wouldn't really help you here, but it makes my usual "...is set primarily by how a function is called..."* statement no longer true. :-)
Upvotes: 4
Reputation: 94101
Because this
depends on the caller. The caller to this.locationHandler
is navigator.geolocation.getCurrentPosition(callback)
and it calls the function like callback()
, notice, there is no dot in that call, meaning there is no value for this
. The solution is to set this
permanently using bind
:
navigator.geolocation.getCurrentPosition(this.locationHandler.bind(this))
Remember, general rule: no dot, no this
, unless you used call
or apply
to call the function, or bind
to set the value of this
forever.
Upvotes: 3