Question 29 / 31

Do constructor-assigned methods still follow the call site?

Topic: this bindingDifficulty: Advanced

Assigning this.foo = function(){...} inside a constructor changes where the function lives, not how its own this gets resolved later — the call-site rule still runs the show.

Solve it with the execution visualizerStep through every binding, function call, and console output below.

Predict the output

Person assigns four methods to this inside its constructor, mirroring the previous four this flavors. Predict all ten console lines produced by calling person1’s methods (and re-calling several against person2).

var name = "Window";

function Person(name) {
    this.name = name;
    this.foo1 = function () {
        console.log(this.name);
    }
    this.foo2 = () => console.log(this.name);
    this.foo3 = function () {
        return function () {
            console.log(this.name);
        }
    }
    this.foo4 = function () {
        return () => console.log(this.name)
    }
}

var person1 = new Person("Person1");
var person2 = new Person("Person2");

person1.foo1();
person1.foo1.call(person2);

person1.foo2();
person1.foo2.call(person2);

person1.foo3()();
person1.foo3.call(person2)();
person1.foo3().call(person2);

person1.foo4()();
person1.foo4.call(person2)();
person1.foo4().call(person2);
Reveal the answer and explanation

Expected output

Person1
Person2
Person1
Person1
Window
Window
Person2
Person1
Person2
Person1

Why this happens

foo1 is an ordinary function assigned to this.foo1 inside the constructor. person1.foo1() uses implicit binding (this = person1, "Person1"); person1.foo1.call(person2) explicitly overrides it ("Person2").

foo2 is an arrow assigned to this.foo2 — created once, while the constructor ran for person1, so it permanently closes over that call’s this (person1). Both person1.foo2() and person1.foo2.call(person2) print "Person1": explicit binding cannot touch an arrow, and unlike q76’s foo2 this arrow really was created with person1 as this, not window.

foo3 returns a brand-new plain function each time it runs. person1.foo3()() and person1.foo3.call(person2)() both call that returned function bare, so default binding applies either way — both print "Window". person1.foo3().call(person2) then explicitly binds the returned function to person2, printing "Person2".

foo4 returns an arrow that DOES call console.log(this.name) this time (unlike q76’s foo4). person1.foo4()() creates and calls the arrow while this = person1 (implicit binding on the foo4() call), printing "Person1". person1.foo4.call(person2)() forces this = person2 for that foo4 call, so the returned arrow closes over person2 and prints "Person2". person1.foo4().call(person2) creates the arrow with this = person1 again, and calling .call(person2) on the finished arrow changes nothing, so it prints "Person1".

What this question tests

  • this.foo = function(){...} inside a constructor is just a normal property assignment — it does not freeze that function’s this the way an arrow does
  • an arrow captures this once, permanently, at the moment it is created — for this.foo2, that moment is "while the Person constructor is running for this particular instance"
  • the same shaped code (foo3/foo4) can behave differently across two questions purely based on whether the returned function is an arrow that logs, an arrow that just returns, or a plain function — read the body, not just the shape

Runtime assumptions

Assumes a classic sloppy-mode script; new Person(...) supplies the constructor call’s this per the [[Construct]] semantics used elsewhere in this series.

Specification references

JavaScript Code EditorDirectly edit or paste custom code
Step 0 of 0
StepSpaceRun
Press → (Step) or Space (Run)
Phase 1: Compilation(Hoisting & Memory Setup)
Phase 2: Execution(Line-by-line Evaluation)
Execution Step Breakdown

Press "→" / click "▶" to step line-by-line, or press "Space" / click "▶ Run" for auto-play.