skip to content

The this Binding

What this refers to is decided at call time by how a function is invoked, not by where it was written — unless it is an arrow. Nearly every JavaScript interview includes one this-value trace.

part ofJavaScriptoverview, primer and where to startread it →
on this pageshow

explore

questions

18

Given `const counter = { count: 0, inc: () => { this.count++; } }`, why does calling `counter.inc()` fail to increment `counter.count`, and what do you change?

level: juniorimportance: must knowfreq 80%

answer

  1. arrows carry no this of their own
  2. object literals are not a scope
  3. this fixed where the arrow is written
  4. receiver is computed then discarded
  5. shorthand method restores the binding

basics

~20 s

Arrow functions have no this of their own. The arrow reads this from the scope where the object literal was written, not from the receiver, so counter.inc() touches that outer this instead of counter. Use a normal method.

solid answer

~40 s

An object literal does not create a scope for `this`, and an arrow function never binds its own `this` — it closes over the `this` of the enclosing lexical scope, fixed at definition time. So `inc` sees whatever `this` was where the literal appears: `undefined` at the top level of an ES module or in strict mode, `globalThis` in a sloppy-mode classic script, `module.exports` in a CommonJS file. Calling it as `counter.inc()` sets the receiver to `counter`, but an arrow simply ignores the receiver, so `this.count++` either throws a TypeError on `undefined` or silently creates a global `NaN`. The fix is a function that does take a receiver: shorthand method syntax `inc() { this.count++; }`, or `inc: function () { ... }`. Alternatively drop `this` and close over `counter` by name.

code

javascript · 14 lines
javascript
const outer = {
  name: 'outer',
  make() {
    return {
      name: 'inner',
      viaArrow: () => this.name,
      viaMethod() { return this.name; }
    };
  }
};

const obj = outer.make();
console.log(obj.viaArrow());  // 'outer'  - lexical this from make()
console.log(obj.viaMethod()); // 'inner'  - receiver at call time

go deeper

for a junior

Be able to say plainly that arrow functions have no this of their own and therefore make poor object methods, and to write the shorthand inc() { ... } fix without hesitation.

for a middle

Explain the mechanism: the object literal is not a scope, so this resolves up the scope chain to the enclosing function or top level, and the receiver computed by the method call is simply discarded.

for a senior

Show why the same defect throws in one file and silently corrupts state in another by naming the top-level this of ES modules, CommonJS files, and sloppy-mode scripts, and say how you would spot it in review.

for a principal

Own the guidance: state where arrows are mandated (callbacks capturing surrounding this) versus banned (anything whose contract is to act on its receiver), and how lint rules and strict-mode-by-default modules keep the silent variant from reaching production.

## The rule in one line A regular function gets a fresh `this` on every call, decided by how it was called. An arrow function has no `this` binding at all: the identifier `this` inside an arrow resolves up the scope chain exactly like any other free variable, landing on the `this` of the nearest enclosing ordinary function, class field initializer, method, or — failing all of those — the top-level `this` of the script or module. ## Why the object literal changes nothing The common wrong model is "the arrow is inside the object, so `this` is the object". Object literals are expressions, not scopes. `{ count: 0, inc: () => ... }` introduces no binding for `this`, no variable environment, nothing the scope chain can stop at. The arrow is written in whatever scope the literal itself sits in, and that is where its `this` comes from. ```javascript // top level of an ES module const counter = { count: 0, inc: () => { this.count++; } }; counter.inc(); // TypeError: Cannot read properties of undefined (reading 'count') ``` The error is not about `counter`. It is about the module's top-level `this`, which the spec defines as `undefined`. ## What the outer `this` actually is The failure mode depends on where the literal lives, which is why this bug shows different symptoms in different files: - ES module top level: `this` is `undefined` → TypeError on property access. - Classic script, sloppy mode, top level: `this` is `globalThis` → no error, it quietly increments `globalThis.count`, which starts as `undefined`, so `undefined++` makes it `NaN`. - CommonJS module top level: `this` is `module.exports` → it mutates the exports object. - Inside another ordinary function called plainly under strict mode: `undefined` again. The silent variants are the dangerous ones: no exception, `counter.count` stays `0`, and the state lands somewhere nobody inspects. ## The call form is irrelevant `counter.inc()` is a method call, so the language does compute a receiver — `counter` — and passes it. An arrow function object has no `[[ThisMode]]` that consumes it; the value is discarded. The same is true for `call`, `apply`, `bind`, and the optional `thisArg` of methods like `Array.prototype.map`. Nothing external can redirect an arrow's `this`. That is the point of arrows, and it is exactly wrong for a method, whose whole contract is "operate on whoever called me". ## Fixes Use a function that participates in receiver binding: ```javascript const counter = { count: 0, inc() { this.count++; } // shorthand method — preferred }; counter.inc(); counter.count; // 1 ``` `inc: function () { this.count++; }` behaves identically for this purpose. If you would rather not depend on the receiver at all, close over the object by name: ```javascript const counter = { count: 0 }; counter.inc = () => { counter.count++; }; ``` That version survives extraction — `const f = counter.inc; f();` still works — because it never used `this`. Note the ordering: the arrow can reference `counter` because the reference is resolved when the arrow runs, after the `const` is initialized. ## Where the same rule is a feature The behaviour that breaks methods is what makes arrows the standard callback form. Inside a real method, a nested arrow inherits the method's `this`, so it keeps working where a nested ordinary function would not: ```javascript const counter = { count: 0, incAll(list) { list.forEach(() => { this.count++; }); // `this` is still counter } }; ``` The distinction to carry into an interview: an arrow is right when you want the `this` of the surrounding code, wrong when you want the `this` of the caller. Object methods and prototype methods want the caller's receiver, so they should be ordinary functions. Callbacks handed to other code usually want the surrounding `this`, so they should be arrows. ## Detecting it in review Two grep-able smells: a top-level arrow whose body mentions `this`, and an object literal property written `name: () => ... this ...`. Both are almost always the bug above. Strict mode helps by turning the silent global-mutation variant into a loud TypeError, which is one more reason modules are easier to reason about than classic scripts.

  • The same object literal is loaded once as an ES module and once as a CommonJS file. Does the arrow-method bug behave the same way?
    No. At the top level of an ES module `this` is `undefined`, so `this.count++` throws a TypeError immediately. At the top level of a CommonJS file `this` is `module.exports`, so the same code runs without error and quietly adds a `count` property to the exports object. Same defect, loud in one host and silent in the other.
  • If the arrow is the wrong tool for a method, why is it the right tool for a callback inside a method?
    Because a callback is invoked by someone else — `forEach`, a timer, an event dispatcher — and that caller supplies a receiver you do not want. An arrow ignores it and keeps the enclosing method's `this`, which is exactly the object you are working on. The property that breaks methods is the property that saves callbacks.
  • Does adding the arrow with `counter.inc = () => { this.count++; }` after the literal change anything?
    No. The arrow still captures the `this` of the scope where the arrow expression is written, and an assignment statement creates no new `this`. The only way to make it work is to stop using `this` and reference `counter` by name, or to use an ordinary function so the receiver is honoured.

An arrow function is like a note that says "give it to my manager" — whoever hands it to you, it always points back to the person who wrote it, never to you.

saying these in an interview costs you the question

  • Thinks the object literal supplies this to the arrow
  • Says an arrow's this is decided at call time
  • Claims counter.inc() rebinds this because of the dot
  • Believes bind on the arrow would fix it
  • Assumes the bug always throws, missing the silent global case

context

open as a page

In JavaScript, what is the difference between Function.prototype.call and Function.prototype.apply, and how does Function.prototype.bind differ from both?

level: juniorimportance: must knowfreq 80%

basics

~20 s

call and apply both invoke the function immediately with an explicit this; call takes the arguments individually, apply takes them as one array-like. bind invokes nothing — it returns a new function whose this is permanently fixed.

open as a page

In JavaScript, counter.increment() works, but `const inc = counter.increment; inc();` throws because this is undefined. Explain why pulling a method out of its object loses the receiver.

level: juniorimportance: must knowfreq 78%

basics

~20 s

JavaScript decides this at call time from whatever sits to the left of the dot, not where the function was written. Extracting a method copies only the function value, so a later plain call has no receiver and this falls back to undefined or globalThis.

open as a page

What does `this` refer to inside a regular JavaScript function that is invoked on its own, as a bare `doThing()`, and how does strict mode change the answer?

level: juniorimportance: must knowfreq 72%

basics

~20 s

A bare call supplies no receiver, so default binding applies: this is globalThis in sloppy mode and undefined in strict mode. Class bodies and ES modules are always strict, so undefined is the modern norm.

open as a page

In JavaScript, if you take a function returned by Function.prototype.bind and then call it with call, apply, or bind again, which this value wins?

level: middleimportance: must knowfreq 62%

basics

~20 s

The first bind wins. bind produces a hard-bound function whose receiver is permanent, so a later call, apply, or bind cannot override it — the extra thisArg is silently ignored, though extra arguments are still passed through.

open as a page

You need to hand an object's method to a callback API such as setTimeout without losing its receiver. Compare binding the method once in the constructor with wrapping the call in an arrow function at the call site.

level: middleimportance: must knowfreq 62%

basics

~20 s

Binding in the constructor stores an own, permanently-bound copy on the instance, giving one stable function you can pass anywhere. An arrow wrapper at the call site leaves the method untouched and simply calls it with the dot, creating a fresh function each time. Prefer the wrapper unless you need a stable identity.

open as a page

In JavaScript, how does the engine decide what `this` refers to inside a regular (non-arrow) function, and in what order do the binding rules apply when more than one could?

level: middleimportance: must knowfreq 78%

basics

~10 s

JavaScript resolves this at the call site, using four rules in priority order: new binding, explicit call/apply/bind, the object before the dot, then the default — globalThis in sloppy mode, undefined in strict mode.

open as a page

Why do `call`, `apply` and `bind` have no effect on an arrow function's `this`, and what happens to the optional `thisArg` argument of `Array.prototype.map` when the callback is an arrow?

level: middleimportance: should knowfreq 50%

basics

~20 s

An arrow has no this binding to overwrite — it reads this from the enclosing scope. So call, apply, bind and the thisArg of methods like map still pass a value, but the arrow never consults it. Arguments are still forwarded normally.

open as a page

Besides having no `this` of their own, what else do JavaScript arrow functions lack compared with ordinary functions, and what happens if you call `new` on one?

level: middleimportance: should knowfreq 62%

basics

~20 s

Arrow functions have no own arguments, no super, no new.target, and no prototype property, and they are not constructors: new on an arrow throws a TypeError. They also cannot be generators. All four missing bindings resolve lexically instead.

open as a page

Why does Array.prototype.slice.call(arguments) turn the arguments object into a real array, and what replaces that idiom in modern JavaScript?

level: middleimportance: should knowfreq 48%

basics

~20 s

Array methods are specified as generic: they read only a length property and integer-keyed properties from whatever this they are given. call supplies the array-like arguments object as that this. Modern code uses a rest parameter, Array.from, or spread instead.

open as a page

How does Function.prototype.bind let you preset arguments as well as this, and what is the trap in its first parameter?

level: middleimportance: should knowfreq 45%

basics

~20 s

Any arguments after bind's first are stored and prepended to every later call, giving partial application. The trap is that the first parameter is always the this value, never an argument — so presetting only arguments requires passing a placeholder receiver such as null.

open as a page

When a JavaScript method is detached and then called as a plain function, this is undefined in some code and globalThis in other code. What decides which, and why is the globalThis outcome the more dangerous bug?

level: middleimportance: should knowfreq 52%

basics

~20 s

Strict mode decides it. In strict code a plain call leaves this as undefined, so the first property access throws immediately; in sloppy code the engine substitutes globalThis, so the method silently reads and writes global properties instead of failing.

open as a page

In a JavaScript call written as `a.b.c.method()`, which object becomes `this` inside `method`, and what rule decides that?

level: middleimportance: should knowfreq 52%

basics

~20 s

Only the immediate base of the property access becomes this, so it is a.b.c — not a. Implicit binding uses the object the method was read from, and everything further left in the chain is irrelevant.

open as a page

In a JavaScript class, what actually differs between defining `handleClick = () => { ... }` as an instance field and defining `handleClick() { ... }` as a prototype method?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The arrow field is an own, enumerable property created fresh on every instance, with this locked to that instance. The method lives once on the prototype, is non-enumerable, and takes this from the receiver at call time.

open as a page

You are writing a generic wrapper that logs every call to a method it is given. How do you make sure the wrapped method still receives the correct this, and where does that approach break down?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Return a regular function that invokes the target with fn.apply(this, args). The wrapper picks up the receiver from its own call site and forwards it. An arrow wrapper cannot do this, and calling fn(...args) drops the receiver entirely.

open as a page

A JavaScript service class passes its own tests but throws "Cannot read properties of undefined" once other modules register its methods as callbacks. How do you confirm the receiver is being lost, and what would you change so this cannot recur across the codebase?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Confirm it by checking the registration sites for a bare method reference and by reproducing the detachment directly, or by asserting the receiver inside the method. Then remove the hazard at the boundary: export closures or pre-bound handlers instead of raw methods, rather than asking every caller to remember.

open as a page

A sloppy-mode function `function User(name){ this.name = name; }` is invoked as `User('ada')` with no `new`. What is `this` inside the call, what happens to the assignment, and why is this bug hard to spot in production?

level: seniorimportance: should knowfreq 45%

basics

~20 s

With no new and no receiver, default binding applies: this is globalThis in sloppy mode, so the assignment creates a global property and the call returns undefined. Nothing throws, so the failure surfaces far from its cause.

open as a page

What happens to the this value stored by Function.prototype.bind when the resulting function is invoked with the new operator?

level: middleimportance: nice to knowfreq 26%

basics

~20 s

The bound this is ignored. Construction creates a fresh object and uses that as the receiver, so new is the one call form a hard binding cannot override. Bound arguments are still prepended, and instanceof against the target function still works.

open as a page