Execution Context in JavaScript - Exercise 3
These methods explicitly choose the receiver for a regular function call; bind returns a new function. This separation helps explain which values and bindings are available at each point in the program.
function introduce() {
return `${this.name} is ready`;
}
const user = { name: 'Ava' };
console.log(introduce.call(user));
console.log(introduce.bind(user)());Arrow functions capture this from their surrounding lexical environment and ignore replacement by call, apply, or bind. This separation helps explain which values and bindings are available at each point in the program.
const user = {
name: 'Mina',
show: () => this.name
};
console.log(user.show()); // not user.nameA constructor call creates a new object, sets its prototype, and calls the constructor with this bound to that new object. This separation helps explain which values and bindings are available at each point in the program.
function User(name) {
this.name = name;
}
const user = new User('Sam');
console.log(user.name);Each recursive call gets a new context with its own parameters and local bindings on the call stack. This separation helps explain which values and bindings are available at each point in the program.
function factorial(number) {
if (number <= 1) return 1;
return number * factorial(number - 1);
}
console.log(factorial(3));
// contexts: factorial(3) -> factorial(2) -> factorial(1)Modules have their own module environment, do not attach top-level bindings to window, and use undefined for top-level this. This separation helps explain which values and bindings are available at each point in the program.
// main.js loaded with type="module"
const secret = 'private';
console.log(this); // undefined
// secret is not a window property.A stack trace lists the active function calls, helping you locate the path that led to an error. This separation helps explain which values and bindings are available at each point in the program.
function load() {
validate();
}
function validate() {
console.trace('validation path');
}
load();Unbounded recursion creates contexts faster than they can return, exhausting the call stack. This separation helps explain which values and bindings are available at each point in the program.
function walk() {
walk();
}
// walk(); // RangeError: Maximum call stack size exceededA closure preserves access to an outer environment after its original function context has returned. This separation helps explain which values and bindings are available at each point in the program.
function makeIdGenerator() {
let id = 0;
return () => ++id;
}
const nextId = makeIdGenerator();
console.log(nextId(), nextId());Each recursive call creates another execution context on the call stack. A base case is required so those contexts can return and the stack can unwind.
function countdown(number) {
if (number === 0) return;
console.log(number);
countdown(number - 1);
}
countdown(3);A closure keeps its outer environment reachable when the inner function is still referenced. This allows private state to persist between calls.
function makeScore() {
let score = 0;
return () => ++score;
}
const nextScore = makeScore();
console.log(nextScore(), nextScore());