skip to content

Given `function Dog(name) { this.name = name; }`, what happens if you call `Dog('Rex')` without `new`, and how does the outcome differ between sloppy mode and strict mode?

level: middleimportance: must knowfreq 60%

answer

  1. capitalisation is not a mechanism
  2. plain call, so undefined comes back
  3. the mode decides what this is
  4. silent global versus immediate throw
  5. modules and class bodies are always strict

basics

~20 s

Without new, Dog runs as a plain function and returns undefined. In sloppy mode this is the global object, so name silently leaks into global state; in strict mode this is undefined and the assignment throws a TypeError immediately.

solid answer

~50 s

A forgotten `new` turns construction into an ordinary call: no object is created, no prototype link is made, and the function's return value — `undefined` here — is what you get. What happens to `this.name = name` depends on the mode. In sloppy mode `this` defaults to the global object, so the assignment quietly creates or overwrites a global `name`, and the bug surfaces far away when `const d = Dog('Rex')` leaves `d` as `undefined` and `d.name` throws "Cannot read properties of undefined". In strict mode `this` stays `undefined`, so the very first assignment throws a `TypeError` at the point of the mistake, which is much easier to debug. That matters in practice because ES modules and class bodies are always strict, while a classic non-module script is sloppy. A `class` constructor is stricter still: calling it without `new` always throws.

code

javascript · 18 lines
javascript
function Dog(name) {
  this.name = name;
}

// Sloppy mode (classic script, no directive):
Dog('Rex');
console.log(globalThis.name);   // 'Rex' — silently written to global state
console.log(Dog('Rex'));        // undefined — nothing was constructed

// Strict mode:
(function () {
  'use strict';
  try {
    Dog.call(undefined, 'Rex'); // same receiver a plain strict call would give
  } catch (e) {
    console.log(e.constructor.name); // 'TypeError'
  }
})();

go deeper

for a junior

Know that without the keyword the function just runs and returns undefined, so the variable you assigned holds undefined and blows up the moment you touch a property on it.

for a middle

Explain the mode split precisely: sloppy substitutes the global object for the receiver so the write succeeds silently, strict leaves it undefined so the assignment throws at once. Name where each mode applies.

for a senior

Demonstrate the diagnosis: reason from an undefined result or a mystery global back to the call site, and say why moving the code to strict mode or a module converts a distant symptom into a local failure.

for a principal

Argue the API-level fix. Decide whether the codebase should expose new-able constructors at all, and weigh factories, classes, and enforced guards against subclassability and the cost of a hardening check in every constructor you ship.

## What the call actually is `Dog('Rex')` is a plain function call. Nothing about the identifier being capitalised, or about `Dog.prototype` existing, changes that — the constructor behaviour lives entirely in the `new` operator, not in the function. So none of the construction steps happen: no object is created, no prototype link is set up, and the value of the expression is whatever the body returns. A constructor body normally returns nothing, so the expression is `undefined`. ```js function Dog(name) { this.name = name; } const d = Dog('Rex'); d; // undefined d.name; // TypeError: Cannot read properties of undefined ``` Notice where the error lands: not on the line with the mistake, but on the line that uses the result. That distance is what makes the bug annoying. ## Sloppy mode: `this` becomes the global object In non-strict ("sloppy") code, a function called with no receiver gets `this` substituted with the global object — `globalThis`. The assignment therefore succeeds, and writes a global: ```js // classic <script>, no "use strict" function Dog(name) { this.name = name; } Dog('Rex'); globalThis.name; // 'Rex' ``` Two things make this worse than a plain crash. First, state is corrupted silently: if two call sites forget `new`, the second overwrites the first, and something else reading that global name gets a value it never expected. Second, in a browser `name` is an existing property of the global object with string semantics, so the write lands somewhere real rather than in a harmless slot — the class of collision you cannot reason about locally. ## Strict mode: `this` stays `undefined` Strict mode removes the substitution. A plain call passes `undefined` as the receiver and it stays `undefined`, so the first property assignment fails immediately: ```js 'use strict'; function Dog(name) { this.name = name; } Dog('Rex'); // TypeError: Cannot set properties of undefined (setting 'name') ``` This is strictly better: the failure is loud, at the offending call, with a stack that points at the caller. It is one of the concrete reasons the mode exists. ## Which mode am I actually in? The answer changes by context, and candidates who only ever write modules often get it wrong when shown a plain script. Code inside an ES module is strict whether or not you say so. Class bodies are strict. A classic script, or a function in a CommonJS file with no directive, is sloppy unless it opts in with `'use strict'`. So the same source text can silently leak a global in one file and throw in another. ## The class case With `class Dog { constructor(name) { this.name = name; } }`, `Dog('Rex')` throws a `TypeError` saying the class constructor cannot be invoked without `new` — before any body code runs, in every mode. Classes closed this hole deliberately; it is one of the behavioural differences between a class and a constructor function, not merely a syntax difference. ## Diagnosing it in real code The symptom you usually meet first is `undefined` where an object was expected, or an instance that mysteriously fails an identity or type check. Useful moves: - Look at the assignment, not the crash site: `const x = Thing(...)` with a capitalised callee and no `new` is the tell. - In sloppy code, check for a global that appeared out of nowhere; a stray `globalThis.name`, `globalThis.id` or similar is a fingerprint of this bug. - Turn the file strict (or move it to a module) and re-run: the failure jumps from far away to the exact line. ## Preventing it Three defences, in ascending order of reliability: put the code in strict mode so the mistake fails loudly; have the function detect that it was called without construction and reject or self-correct; or use a `class`, which enforces the rule for you. A fourth option is to stop exposing a constructor at all and export a factory function that internally constructs and returns the object — then there is no `new` for a caller to forget, at the cost of giving up the constructor surface. ## Why this is asked It is a compact test of three separate pieces of knowledge at once: that `new` and not the function carries the construct behaviour, that `this` binding for a plain call differs by mode, and that a function with no `return` yields `undefined`. A candidate who can walk from the forgotten keyword to the global write to the far-away `TypeError` has a working mental model rather than a memorised rule.

  • Why does the same forgotten `new` throw immediately in one file and corrupt a global in another?
    Because the two files differ in mode. In sloppy mode a call with no receiver substitutes the global object for `this`, so the assignment succeeds and writes a global. In strict mode no substitution happens, `this` is `undefined`, and the first property write throws. ES modules and class bodies are strict by default, classic scripts are not.
  • If the value came back as `undefined`, how would you track down which call site forgot the keyword?
    Work backwards from the assignment rather than the crash. Find where the `undefined` was produced — a capitalised callee with no `new` is the signature — then run the file in strict mode or as a module so the failure moves to the offending line. In sloppy code, an unexpected global property with a constructor-ish name is a second fingerprint.
  • Does the same trap exist for `class Dog { ... }`?
    No. Invoking a class constructor without `new` throws a `TypeError` before any body code runs, in every mode, because a class constructor deliberately refuses ordinary calls. That is a behavioural guarantee a plain constructor function does not give you, and it is one reason to prefer `class` for types callers construct.

saying these in an interview costs you the question

  • Claims the call still returns a new object because the name is capitalised
  • Says this is undefined in every mode, so it always throws
  • Reports the failure as a ReferenceError rather than a TypeError
  • Assumes a plain script file is strict by default
  • Thinks the error appears on the line that forgot the keyword in sloppy mode

context