Aside from the callback ordering issue mentioned by Thierry, there is a missing this
context on this specific line:
navigator.geolocation.getCurrentPosition(this.showPosition);
The Challenge
You are facing the common JavaScript problem known as the incorrect this
context.
The behavior of the this
keyword in JavaScript differs from languages like C# and Java.
Understanding this
In a function, the this
keyword is determined as follows:
* If the function was created through .bind
, the this
value is the argument provided to bind
* If the function was called as a method, e.g. expr.func(args)
, then this
is expr
* Otherwise
* In strict mode, this
is undefined
* Otherwise, this
is window
(in a browser)
Let's see this concept in action:
class Foo {
value = 10;
doSomething() {
// Displays 'undefined', not '10'
console.log(this.value);
}
}
let f = new Foo();
window.setTimeout(f.doSomething, 100);
This code will display undefined
(or throw an exception in strict mode).
The doSomething
function lost its this
context due to how it was invoked without consideration of the correct context.
One red flag indicating such problems is when you encounter code like this:
class Foo {
value = 10;
method1() {
doSomething(this.method2); // WARNING, referencing method without invoking it
}
method2() {
console.log(this.value);
}
}
The Solution
There are several solutions available, each with its own pros and cons.
The ideal approach depends on how frequently the method is called from different locations.
Arrow Function in Class Definition
Instead of the regular method syntax, use an arrow function to initialize instance-specific members.
class DemonstrateScopingProblems {
private status = "blah";
public run = () => {
// Works fine
console.log(this.status);
}
}
let d = new DemonstrateScopingProblems();
window.setTimeout(d.run); // No issues
- Advantage: Ensures the correct closure per instance for methods frequently used in callback positions.
- Advantage: Prevents forgetting to handle
this
context
- Advantage: TypeScript type-safety
- Advantage: Simplifies handling functions with parameters
- Disadvantage: Limitation in calling parent class methods using
super.
- Disadvantage: Creates additional non-typesafe contracts among classes
Function Expression at Reference Site
An example demonstrating this with dummy parameters:
class DemonstrateScopingProblems {
private status = "blah";
public something() {
console.log(this.status);
}
public run(x: any, y: any) {
// Fine
console.log(this.status + ': ' + x + ',' + y);
}
}
let d = new DemonstrateScopingProblems();
// With parameters
someCallback((n, m) => d.run(n, m));
// Without parameters
window.setTimeout(() => d.something(), 100);
- Advantage: Offers memory/performance balance compared to arrow function method
- Advantage: 100% type safety in TypeScript
- Advantage: Compatible with ECMAScript 3
- Advantage: Requires minimal typing of the instance name
- Disadvantage: Parameter redundancy
- Disadvantage: Limited support for variadic parameters