skip to content

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%

answer

  1. only one object survives the chain
  2. look at the last dot
  3. base of the property reference
  4. outer namespaces are not inherited
  5. brackets and array indexes behave identically

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.

solid answer

~40 s

`this` is `a.b.c`, the object immediately to the left of the final dot. Implicit binding works from the property reference the call target evaluated to: the engine keeps the *base* of that reference and passes it as the receiver. `a` and `a.b` were only steps along the way to producing that base; once `a.b.c` is in hand they play no further role. The same holds for bracket access and array elements — in `rows[0].render()` the receiver is `rows[0]`, not `rows`. The practical consequence is that a method nested deep in a config or namespace object can only see the properties of its immediate parent through `this`, which surprises people who expect the whole namespace to be reachable.

code

javascript · 10 lines
javascript
const config = {
  env: 'prod',
  db: {
    host: 'db.internal',
    url() { return `${this.env}://${this.host}`; }
  }
};

console.log(config.db.url()); // 'undefined://db.internal'
// this is config.db, so this.env misses — the outer object is not reachable.

go deeper

for a junior

Practise reading a call site backwards: find the last dot before the parentheses, and the object on its left is this.

for a middle

Explain implicit binding as the base of the property reference at the call site, and show that bracket access, array indexes and call results all produce a base the same way.

for a senior

Recognise the namespaced-config bug on sight — a nested method reading this.someOuterField and silently getting undefined — and explain why restructuring beats reaching for a binding trick.

for a principal

Be ready to discuss API shape: deeply nested namespace objects whose methods depend on this are fragile, and you should be able to justify flatter designs or explicit context parameters instead.

## The answer, and the rule behind it In `a.b.c.method()`, `this` inside `method` is `a.b.c`. The rule is **implicit binding**: when the thing being called is a property access, the receiver is the object that property was read from. The spec expresses this in terms of a *Reference* — evaluating `a.b.c.method` produces a Reference whose **base** is the value `a.b.c` and whose referenced name is `method`. The call then passes that base as `this`. Nothing else about how the base was reached is retained. ## Why the outer objects vanish Read the expression the way the engine does, left to right: 1. Evaluate `a` → an object. 2. Read property `b` from it → another object. `a` is now finished with. 3. Read property `c` from that → another object. `a.b` is now finished with. 4. Form the reference `<base = a.b.c, name = 'method'>` and call it. By step 4 the only object still in play is `a.b.c`. There is no chain of receivers, no parent pointer, and no way for `method` to walk back up to `a`. ```js const app = { name: 'app', db: { name: 'db', users: { name: 'users', who() { return this.name; } } } }; app.db.users.who(); // 'users' ``` ## The same rule in other syntactic shapes Implicit binding does not care which access syntax produced the base: ```js const key = 'users'; app.db[key].who(); // 'users' — bracket access, same base const rows = [{ id: 7, show() { return this.id; } }]; rows[0].show(); // 7 — the receiver is rows[0], not rows function pick() { return app.db.users; } pick().who(); // 'users' — the base is whatever the call returned ``` That last one is worth pausing on: the base does not have to be written as a variable path at all. Any expression that yields an object, followed by a dot and a call, supplies that object as the receiver. ## Where this bites in real code The common surprise is a namespaced configuration or service object: ```js const config = { env: 'prod', db: { host: 'db.internal', url() { return `${this.env}://${this.host}`; } // this.env is undefined } }; config.db.url(); // 'undefined://db.internal' ``` The author assumed `this` would be `config` because that is what they typed first. It is `config.db`, so `this.env` misses. There is no inheritance of receivers down a chain: each level's methods see only their own object (plus whatever it inherits through its prototype chain, which is a separate mechanism). The fix is to stop relying on `this` reaching further than it can — reference the outer object by name, or restructure so the data the method needs lives on the object the method hangs off. ## A second-order point: getters count too If `c` is an accessor property, its getter runs during step 3 and whatever it returns becomes the base: ```js const holder = { get current() { return { id: 42, read() { return this.id; } }; } }; holder.current.read(); // 42 — the base is the object the getter returned ``` A fresh object is produced on each access, so `holder.current.read` and a later `holder.current.read` are methods on two different receivers. ## What the interviewer is checking They want to hear "the object immediately before the dot", plus the reason: implicit binding uses the base of the property reference at the call site. Candidates who answer `a` are pattern-matching on the leftmost identifier, which is exactly the mistake that produces the `undefined://` style bug above.

  • What is `this` in `rows[0].render()`?
    It is `rows[0]` — the element object — not the array. Bracket access forms exactly the same kind of property reference as a dot, so the base is whatever the index resolved to. The array is only the route used to reach that object.
  • Can a nested method reach an outer namespace object through `this` in any way?
    No. Receivers are not chained: `this` is only the immediate base. A method reaches an outer object by closing over it by name, by being handed it as an argument, or by having the needed data on its own object. The prototype chain is a different mechanism and does not link a nested object to its container.
  • Does it matter whether the base came from a variable path or from a function call?
    No — any expression that evaluates to an object works. In `pick().who()` the receiver is whatever `pick()` returned. The engine only needs a base value plus a property name; how that base was produced is irrelevant to binding.

saying these in an interview costs you the question

  • Says this is the outermost object in the chain
  • Believes receivers are inherited down nested objects
  • Thinks bracket access binds differently from dot access
  • Assumes a nested method can read its container's properties via this
  • Confuses the property chain with the prototype chain

context