Question 28 / 31
Which of these four this flavors can .call() actually override?
Topic: this bindingDifficulty: Advanced
An ordinary method, an object-literal arrow, and two functions-that-return-functions each resolve this differently — and one of the four never even reaches a console.log.
Solve it with the execution visualizerStep through every binding, function call, and console output below.Predict the output
This code makes ten calls through person1's four methods, but only seven of them ever print anything. Predict the seven lines, in order, and notice which three calls stay silent.
var name = "Window";
var person1 = {
name: "Person1",
foo1: function () {
console.log(this.name);
},
foo2: () => console.log(this.name),
foo3: function () {
return function () {
console.log(this.name)
}
},
foo4: function () {
return () => this.name
}
}
var person2 = { name: "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
Window
Window
Window
Window
Person2Why this happens
foo1 is an ordinary method. person1.foo1() uses implicit binding (this = person1, "Person1"); person1.foo1.call(person2) explicitly overrides that receiver ("Person2").
foo2 is an arrow function written directly as the object literal's property value — its this was captured once, when the object literal itself was evaluated at the top level, so this is window there. Both person1.foo2() and person1.foo2.call(person2) print "Window": an arrow's this can never be overridden, and it was never person1's this to begin with — arrows are not methods.
foo3 is an ordinary method that returns a brand-new plain function. person1.foo3()() and person1.foo3.call(person2)() both call that returned function bare, so default binding applies regardless of how foo3 itself was invoked — both print "Window". person1.foo3().call(person2) then explicitly calls the returned function with person2, printing "Person2".
foo4 looks like foo3 but its returned value is an arrow — () => this.name — which returns a string instead of logging it. All three foo4 calls (person1.foo4()(), .call(person2)(), .call(person2)) evaluate a value that is immediately discarded, so none of them print anything at all.
What this question tests
- an ordinary method's this is decided at the call site, independently on every call — the same method can bind this differently each time it runs
- an arrow used as an object literal property is not a method: its this is whatever was in scope when the object literal was evaluated, not the object it ends up living on
- call()/apply() only ever affect an ordinary function's this; applying them to an arrow is accepted syntactically but changes nothing
- a function that returns a value instead of calling console.log produces no visible output, even though it still runs and its this still resolves
Runtime assumptions
Assumes a classic sloppy-mode script, where a receiver-less call defaults this to the global object (window) and the object literal is evaluated at the top level.