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?
answer
- no rule matched, so the fallback fired
- the write still succeeds
- two failures from one missing keyword
- the caller receives undefined
- strict mode makes it crash instead
basics
~20 sWith 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.
solid answer
~40 sWithout `new`, the call site supplies no receiver, so the highest-priority rule never fires and default binding takes over. In sloppy mode that means `this === globalThis`, so `this.name = name` writes a property onto the global object instead of onto a new instance, and the function's implicit `undefined` return becomes the caller's value. Both failures are silent: state leaks process-wide and can collide with a property the host already defines, while the caller happily stores `undefined` and only blows up later when it dereferences it — often in a different module, with a stack trace that points nowhere near the missing `new`. Under strict mode the same call throws a TypeError immediately, because `this` is `undefined`, which is exactly why classes and modern factory functions fail loudly instead.
code
javascript · 13 lines// Sloppy-mode script.
function User(name) { this.name = name; }
const u = User('ada'); // no `new`
console.log(u); // undefined -> the caller stores a non-object
console.log(globalThis.name); // 'ada' -> the write leaked to the global object
// Explicit guard for legacy constructor-shaped functions:
function SafeUser(name) {
if (new.target === undefined) return new SafeUser(name);
this.name = name;
}
console.log(SafeUser('ada').name); // 'ada'go deeper
Know that a constructor-style function must be called with new, and that calling it without new does not give you an object back.
Explain that the missing new drops the call to default binding, so the assignment lands on the global object in sloppy mode and the expression evaluates to undefined.
Demonstrate the diagnosis: an undefined value with no error at the assignment, plus a stray global, and a stack trace pointing at innocent code. Explain why strict mode's TypeError is the outcome you want.
Be ready to argue for removing the failure mode entirely — classes or factory functions that never touch this — rather than adding runtime guards, and to justify enforcing strictness across the codebase.
## Walking the precedence table The call is `User('ada')`. Apply the four binding rules in order: 1. **new binding** — there is no `new`. Skipped. 2. **Explicit binding** — no `.call`, `.apply`, or bound function. Skipped. 3. **Implicit binding** — no dot before the parentheses. Skipped. 4. **Default binding** — this is what fires. So `this` is whatever default binding yields for a sloppy-mode function: `globalThis`. ## What actually happens, line by line ```js function User(name) { this.name = name; } const u = User('ada'); // no new console.log(u); // undefined console.log(globalThis.name); // 'ada' — the write landed on the global object ``` Two distinct defects come out of one missing keyword: - **The write goes to the wrong object.** `this.name = name` succeeds — it just succeeds against the global object. Nothing throws, nothing warns. - **The call evaluates to `undefined`.** A constructor body normally has no `return`, and without `new` there is no fresh object to hand back, so the expression's value is `undefined`. ## Why it is hard to find Every symptom appears at a distance from the cause. The caller stores `undefined` in a variable it believes holds an object. That variable may be passed around, put in an array, stored in a map, and only dereferenced three modules later — at which point you get `TypeError: Cannot read properties of undefined (reading 'name')` pointing at code that is entirely innocent. The stack trace does not contain the line that forgot `new`. Meanwhile the global object has quietly acquired a property. Because it is global, it is shared by everything in the realm: a second call overwrites it, unrelated code can read it, and it can collide with a name the host environment already defines on the global object, giving behaviour that looks unrelated to your feature. Worse, the bug is *state-dependent* — a later, correct `new User(...)` still works, so the failure is intermittent and hard to reproduce from a test that exercises the happy path. ## The strict-mode contrast Make the same function strict and the picture changes entirely: ```js 'use strict'; function User(name) { this.name = name; } User('ada'); // TypeError: Cannot set properties of undefined (setting 'name') ``` Default binding now yields `undefined`, so the very first property write throws, at the exact call, with the offending line in the trace. Nothing leaks and nothing is left half-initialised. This is one of the clearest arguments for strict mode: it converts an invisible data-corruption bug into an ordinary, locally-diagnosable crash. ## Why modern code rarely hits it Three things have largely retired this bug: - **`class`** — a class constructor cannot be invoked without `new`; the call throws a `TypeError` on the spot. Class bodies are also strict. - **ES modules** — module code is always strict, so any constructor-shaped function in a module gets the `undefined` receiver and the loud failure. - **`new.target`** — inside a regular function this is `undefined` when called without `new` and the constructor otherwise, so legacy code can guard explicitly: ```js function User(name) { if (new.target === undefined) return new User(name); this.name = name; } ``` That guard makes the function work either way, which was a common defensive pattern before classes existed. ## How to diagnose it when you meet it Symptoms that should send you looking for a missing `new`: an object-typed value that is `undefined` with no error at the assignment; a property appearing on `globalThis` that nobody declared; a field whose value survives across operations that should each have their own instance. In a debugger, breaking at the top of the constructor and inspecting `this` settles it in one step — if `this` is the global object, no `new` was used. The long-term fix is not a runtime guard but removing the ambiguity: convert to a `class`, or to a factory function that returns an object literal and never touches `this` at all. Both make the call form impossible to get wrong.
- What happens if that same code is inside a class rather than a plain function?You cannot make the mistake: invoking a class without `new` throws a TypeError immediately, regardless of the body. Class bodies are also strict, so even the underlying default binding would give `undefined` rather than the global object. That is a strong reason to prefer classes over constructor-shaped functions.
- How would you detect the missing `new` at runtime inside a legacy function?Check `new.target`: it is `undefined` for an ordinary call and the constructor function for a construct call. Legacy code either throws on that condition or self-corrects with `return new Fn(...)`. Both make the intent explicit rather than relying on whatever `this` happens to be.
- Why does the caller see `undefined` rather than the object it wanted?Because there was no construct call, nothing created an object to hand back, and the body has no explicit `return`. A plain function call evaluates to its return value, which here is the implicit `undefined` — so the caller stores a non-object and fails later when it dereferences it.
saying these in an interview costs you the question
- Says the call throws immediately in sloppy mode
- Thinks a constructor always returns a new object
- Believes this points at the function when new is missing
- Assumes forgetting new merely wastes an allocation
- Claims strict mode makes no difference here