Initially, the inquiry provided did not include this
at all, which is intriguing considering it is the singular exception to the issue at hand.
In JavaScript, lexical scope reigns supreme. This means that functions can access the scope in which they are defined, rather than accessing values randomly scattered throughout the current stack.
const rememberX = (x) => () => x;
// a function that accepts a value and returns another function
// when the inner function is called, it returns the original value
let x = 5;
let x42 = rememberX(42);
let x10 = rememberX(10);
x = 20;
x42(); // 42
x10(); // 10
The magic of closure stems from lexical scoping, enabling the functionality seen above. This distinction sets JavaScript apart from other languages under normal circumstances, leaning more towards functional programming principles.
Curiously, this
breaks this rule as the sole outlier. It defies the norm by being exceptionally late-binding and determined at the exact moment of execution.
class Person {
constructor (name) {
this.type = "person";
this.name = name;
}
sayHi () {
console.log(`${this.name}, the ${this.type}, says “Hi”.`);
}
}
const bob = new Person("Bob");
bob.sayHi(); // "Bob, the person, says “Hi”."
const spot = {
type: "dog",
name: "Spot",
sayHi: bob.sayHi
};
spot.sayHi(); // "Spot, the dog, says “Hi”."
bob.sayHi.apply({
type: "ape",
name: "Grape"
}); // "Grape, the ape, says “Hi”."
// apply is a method that all functions have,
// which sets `this` for this invocation
const bind = function (method, overriddenThis) {
return function (...args) {
return method.apply(overriddenThis, args);
};
};
// bind also exists as a method on functions,
// to prevent `this` from being overwritten, using closure
const sayHi = bind(bob.sayHi, bob);
sayHi.apply(spot); // "Bob, the person, says “Hi”."
This explains why your stack-based calls may not yield the expected results, sheds light on the behavior of this
, and offers solutions to navigate around it.