jskidd3
jskidd3

Reputation: 4783

JavaScript OOP 'this' scope not working

Map class:

function Map() {
    this.map;
    this.userLatitude = 0;
    this.userLongitude = 0;

    this.startPositionReporting();
}

Map.prototype.getUserPosition = function () {
    alert(this.userLatitude);

    return { 
        latitude: this.userLatitude,
        longitude: this.userLongitude
    };
};

Map.prototype.startPositionReporting = function () {
    var geolocationOptions = {
        enableHighAccuracy : true
    };

    function showError(error) {
        switch(error.code) {
            case error.PERMISSION_DENIED:
                alert("User denied the request for Geolocation.");
                break;
            case error.POSITION_UNAVAILABLE:
                alert("Location information is unavailable.");
                break;
            case error.TIMEOUT:
                alert("The request to get user location timed out.");
                break;
            case error.UNKNOWN_ERROR:
                alert("An unknown error occurred.");
                break;
        }
    }

    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
            this.userLatitude = position.coords.latitude;
            this.userLongitude = position.coords.longitude;
        }, null, geolocationOptions);
    }
};

Calling the object:

var mapObject = new Map();
console.log(mapObject.getUserPosition()); // Why does this return 0???

I can't figure out why mapObject.getUserPosition() is returning 0. I have checked this.userLatitude and this.userLongitude inside the startPositionReporting method and they work fine. This must be something to do with scope... any ideas?

Upvotes: 0

Views: 71

Answers (4)

LetterEh
LetterEh

Reputation: 26706

Several. The problem is that this doesn't behave the way you think it does.

this is bound to a function's scope, and is resolved dynamically (at the time the function is called).

Your problem is that you've got a callback fed to navigator.geolocation.getCurrentPosition, where, in that function, you're setting properties on this, expecting this to be your instance.

It's not.

this is most-likely window, in your case.

Two solutions are:

Map.prototype.startPositionReporting = function () {
    var map = this; // instance of Map

    /* ...  */
    navigator.geolocation.getCurrentPosition(function (position) {
        map.userLatitude = position.coords.latitude;
        // ...
    });

or

var updateMapCoords = function (position) {
        this.userLatitude = position.coords.latitude;
        // ...
};


Map.prototype.startPositionReporting = function () {
    var map = this; // instance of Map
    var updateMap = updateMapCoords.bind(map);
    // returns a copy of the function with `this` set to the value passed to `bind`
    // ...

    navigator.geolocation.getCurrentPosition(updateMap);
    // ...

Upvotes: 2

jakee
jakee

Reputation: 18566

Your problem is simple

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
        this.userLatitude = position.coords.latitude;
        this.userLongitude = position.coords.longitude;
    }, null, geolocationOptions);
}

In this part of the startPositionReporting -method you assume that inside your anonymous function the this variable still references your Map object. Well, it doesn't. You need to explicitly store the scope you want to use in a variable and then use it within the anonymous function closure, like this:

var self = this;

if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
        self.userLatitude = position.coords.latitude;
        self.userLongitude = position.coords.longitude;
    }, null, geolocationOptions);
}

Hope this helps!

Upvotes: 0

milagvoniduak
milagvoniduak

Reputation: 3254

Yes it is the scope problem.

  var that = this;

  if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
            that.userLatitude = position.coords.latitude;
            that.userLongitude = position.coords.longitude;
        }, null, geolocationOptions);
    }

The function that you pass in getCurrentPosition will be invoked in a different context. That is why you need to save off the reference to the context where that function was invoked.

Upvotes: 2

Colin DeClue
Colin DeClue

Reputation: 2244

navigator.geolocation.getCurrentPosition is an asynchronous function. You need to wait until it returns before those fields will be anything other than 0.

Upvotes: 0

Related Questions