I am currently learning OOP in JavaScript. I'm refactoring an app and I have a problem.
I have created a Class "Weather"
class Weather {
constructor({longitude, latitude} = {}) {
this.longitude = longitude;
this.latitude = latitude;
this.options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
}
getLongitude(){
return this.longitude;
}
setLongitude(longitude){
this.longitude = longitude;
}
getLatitude(){
return this.latitude;
}
setLatitude(latitude){
this.latitude = latitude;
}
getLocation() {
if (Modernizr.geolocation) {
//if locatin is enabled, show position in button
navigator.geolocation.getCurrentPosition(this.success, this.fail, this.options);
} else {
alert("Sorry, you browser doesn't have geolocation");
}
}
success(position){
let pos = position.coords;
console.log('Your actual position is :');
console.log(`Latitude : ${pos.latitude}`);
console.log(`Longitude: ${pos.longitude}`);
console.log(`More or less ${position.coords.accuracy} meters.`);
this.setLongitude(pos.longitude); // <== Doesn't work
this.setLatitude(pos.latitude); // <== Doesn't work
}
fail(){
console.log('User refused to give position');
}
}
Everything works, I can retrieve longitude and latitude like that
let City = new Weather();
City.getLocation(); //will call getLocation and on success, it will console.log the longitude and latitude
My problem is that I can update the value of my object. When I create my object, the constuctor defines a longitude and latitude if they are passed as argument. However, in the success method, I cannot reassign value of the object.
Is there any solution ?
4 Answers 4
You've lost the context because the success method is passed as a reference. So the value of this when it is called does not refer to the Weather instance. You can use Function.prototype.bind method to fix this:
class Weather {
...
getLocation() {
if (Modernizr.geolocation) {
//if locatin is enabled, show position in button
navigator.geolocation.getCurrentPosition(
this.success.bind(this),
this.fail,
this.options
);
} else {
alert("Sorry, you browser doesn't have geolocation");
}
}
...
}
Alternatively, you can bind the method in the constructor, so that bind is only called once when the object is instantiated.
Comments
You can use getters and setters of class javascript.
class Weather {
constructor({longitude, latitude} = {}) {
this.longitude = longitude;
this.latitude = latitude;
...
}
get Latitude(){return this.latitude};
set Latitude(args){this.latitude=args};
...
}
Comments
First and foremost: Whilst it is still possible to mimic OOP in JavaScript, you should avoid this approach and instead opt for Functional Programming.
To answer your question: When you are invoking success(), as it happens on every function in JS, the context changes and this will be referring to wherever you invoke the code.
To solve this issue you can either use bind(this) or change success to a method instead of a function. Here is how it can be done:
successs: function(position){
let pos = position.coords;
console.log('Your actual position is :');
console.log(`Latitude : ${pos.latitude}`);
console.log(`Longitude: ${pos.longitude}`);
console.log(`More or less ${position.coords.accuracy} meters.`);
this.setLongitude(pos.longitude); // This will work now because this
this.setLatitude(pos.latitude); // will be referring to the Weather object
}
For further information on how this works in JS please have a look at this explanation: http://blog.andrewray.me/react-es6-autobinding-and-createclass/
Comments
As Chris Walks say you are having troubles with the "this" value inside succes s function
Ther are several ways to solve it, the problem is when navigator.geolocation.getCurrentPosition calls success, it sets a different "this" value that you expect on the function
One could be
var me = this;
navigator.geolocation.getCurrentPosition(function(position) {
me.success(position)
}, this.fail, this.options)
You will have the same proble with this.fail
Another way is
navigator.geolocation.getCurrentPosition(this.success.bind(this), this.fail.bind(this), this.options)
You can learn more from here
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this
Edited: As sugested first solution is wrong, a pointer to this is need
1 Comment
this unless you use a var me = this;. You could use an arrow function for the wrapper instead to avoid that problem.
thisvariable, common practice is to put avar self = this;at the top, and use self where you want the class this reference.success(position){bla bla}.bind(this);