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.

javascript

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.

javascript

const user = {
  name: 'Mina',
  show: () => this.name
};
console.log(user.show()); // not user.name

A 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.

javascript

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.

javascript

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.

javascript

// 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.

javascript

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.

javascript

function walk() {
  walk();
}
// walk(); // RangeError: Maximum call stack size exceeded

A 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.

javascript

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.

javascript

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.

javascript

function makeScore() {
  let score = 0;
  return () => ++score;
}
const nextScore = makeScore();
console.log(nextScore(), nextScore());
All sections expanded. Activate the control to collapse all sections.