skip to content

In a strict-mode script, what is `this` inside a function invoked as a plain `f()`, and why does the identical code see the global object when the script is not strict?

level: middleimportance: must knowfreq 70%

answer

  1. a plain call passes undefined
  2. sloppy mode substitutes and boxes
  3. strict mode passes the value through
  4. the callee's strictness decides, not the caller's
  5. forgotten new throws instead of writing a global

basics

~20 s

In strict mode this is undefined in a plain f() call. Sloppy mode substitutes the global object whenever the incoming this value is undefined or null, and wraps a primitive this value in an object — strict mode passes it through untouched.

solid answer

~50 s

A plain call passes no this value, so the incoming value is `undefined`. What happens next depends on whether the *called* function is strict. A strict function takes the value as it arrives, so `this` is `undefined`. A sloppy function goes through a coercion step the spec applies when binding `this`: `undefined` or `null` is replaced with the global object, and a primitive such as a string or number is boxed into its wrapper object. That is why the same body sees `globalThis` in a classic script and `undefined` in strict code, and why `f.call('abc')` gives you the primitive `'abc'` under strict mode but a `String` object without it. The practical payoff is that a constructor or method called incorrectly throws `TypeError: Cannot set properties of undefined` immediately, instead of quietly writing its fields onto the global object.

code

javascript · 9 lines
javascript
"use strict";

function whoAmI() {
  return this;
}

console.log(whoAmI());
console.log(whoAmI.call(null));
console.log(typeof whoAmI.call("hi"));

go deeper

for a junior

Know that a function called as plain f() sees this as undefined in strict mode and as the global object otherwise, and that calling a constructor without new therefore throws instead of doing nothing visible.

for a middle

Explain the actual step: non-strict functions substitute the global object for null or undefined and box primitives, and strict functions skip both. Note that method calls and new are unaffected.

for a senior

Demonstrate the debugging value — a forgotten new or a detached method now fails at the mistake with a TypeError instead of leaking state onto the global object, and you can say how you would find such a leak in legacy sloppy code.

for a principal

Own the boundary policy: whether shared library code guarantees strict semantics regardless of how a host page loads it, and what you rely on — modules, per-function directives, or wrappers — so callers never depend on the substitution behaviour.

## What a plain call actually passes When you write `f()`, no receiver is supplied, so the call passes `undefined` as the this value. That is true in every dialect. The interesting part is what the function does with it when it sets up its execution context. The specification's step here is the one that binds `this` for an ordinary function. It branches on the function's own strictness: - **Strict function.** The incoming this value is stored exactly as it arrived — no substitution, no conversion. `undefined` stays `undefined`; `null` stays `null`; a string stays a primitive string. - **Sloppy function.** Two coercions apply. If the this value is `undefined` or `null`, it is replaced with the global object. Otherwise it is passed through `ToObject`, which boxes primitives — a string becomes a `String` object, a number becomes a `Number` object. So the entire difference is one coercion step that strict mode removes. ```javascript "use strict"; function whoAmI() { return this; } console.log(whoAmI()); // undefined console.log(whoAmI.call(null)); // null console.log(typeof whoAmI.call("hi")); // "string" ``` Without the directive the same three lines print the global object, the global object, and `"object"`. ## Whose strictness decides This catches people out: it is the strictness of the **function being called**, not of the call site. A sloppy function invoked from inside strict code still gets the global-object substitution, because its own body was parsed under the old rules. Strictness is a lexical property fixed where the function is written. ```javascript // sloppy file function legacy() { return this; } function modern() { "use strict"; return legacy(); // the global object, not undefined } ``` Since class bodies and module code are strict automatically, methods you write in a class already behave the strict way with no directive in sight. ## Why the sloppy behaviour is dangerous The substitution turns a mistake into a silent side effect. Consider a function that assigns to `this`: ```javascript function Counter() { this.total = 0; } Counter(); // forgot new ``` In sloppy mode `this` is the global object, so `total` becomes a global property. Nothing throws. Later code reads `counter.total` and gets `undefined`, or worse, two independent "instances" scribble over the same global. In strict mode `this` is `undefined` and the very first assignment throws `TypeError: Cannot set properties of undefined (setting 'total')`, pointing straight at the missing `new`. The boxing half has its own cost. In a sloppy function called as `f.call(5)`, `this` is a `Number` *object*, so `this === 5` is false, `typeof this` is `"object"`, and any property you set on it is discarded when the wrapper is thrown away. Strict mode hands you the primitive, which is almost always what the author meant. ## What is unaffected Strict mode changes only the substitution step, so: - **`new f()`** still creates a fresh object and binds it as `this`, in both dialects. - **`obj.m()`** still binds `obj`, in both dialects. - **`f.call(obj)` with an object argument** binds that object, in both dialects — the coercion only matters for `null`, `undefined`, and primitives. - **Arrow functions** have no this binding of their own at all, so this whole branch never runs for them; they read `this` from the enclosing scope regardless of dialect. ## Reading `this` at top level A related confusion: at the top level of a classic script, `this` is the global object even under `"use strict"`, because that value comes from the script's global environment rather than from a function call. Strict mode does not change it. What changes is only the value seen *inside* a function that was called without a receiver. ## How to answer well Name the mechanism — the substitution and boxing step that the spec applies only for non-strict functions — rather than reciting "strict mode makes `this` undefined". Then give the payoff: the mistake that used to write to the global object now throws at the point of the mistake. If you can add that the *callee's* strictness decides, and that `new` and method calls are untouched, you have covered everything an interviewer is listening for.

  • A sloppy-mode function is called from inside a strict-mode function. Which dialect's rule applies to its `this`?
    The callee's. Strictness is lexical and fixed where a function is written, so a sloppy function still gets the substitution — `this` is the global object even though the caller is strict. The reverse holds too: a strict function called from sloppy code still sees `undefined`. Nothing about `this` binding is inherited across a call boundary.
  • Under "use strict", what does `this` refer to at the top level of a classic script?
    Still the global object. That value comes from the script's global environment record rather than from a function call, so the substitution rule never enters into it and strict mode leaves it alone. Only the value inside a function invoked without a receiver flips to `undefined`. (Module top-level `this` is a separate matter, decided by the module system rather than by the directive.)
  • Why does `f.call(5)` give you an object in sloppy mode but the number 5 in strict mode?
    Because the sloppy path runs the incoming this value through `ToObject`, which boxes a primitive into its wrapper — here a `Number` object, so `this === 5` is false and `typeof this` is `"object"`. Strict mode skips that conversion and stores the primitive as given. Any property you set on the sloppy wrapper is also lost, since the wrapper is discarded after the call.

saying these in an interview costs you the question

  • Says strict mode makes this undefined everywhere
  • Thinks the caller's strictness decides the binding
  • Claims new stops working without a receiver
  • Believes primitives can never be a this value
  • Confuses undefined this with an arrow function's lexical this

context