intermediateCommon5 min read · Updated Jul 19, 2026

Implement the new Operator from Scratch

Implement myNew(Constructor, ...args) in JavaScript: prototype linking with Object.create, this binding, and the return-override rule that explains constructor quirks.


The problem

Without using new, implement myNew(Constructor, ...args) that behaves identically — including the obscure rule about constructors that return something.

new looks atomic; it's actually four spec steps, and this question checks whether you can name all four. It's the natural companion to custom bind (whose hardest edge case is new) and prototypal inheritance (whose machinery this operator drives).

The four steps:

  1. Create a fresh object whose prototype is Constructor.prototype.
  2. Call Constructor with this bound to that object.
  3. If the constructor returns an object, that object wins.
  4. Otherwise, return the created object (any primitive return is ignored).

Implementation

function myNew(Constructor, ...args) {
  if (typeof Constructor !== "function") {
    throw new TypeError(`${Constructor} is not a constructor`);
  }
 
  // Step 1: fresh object, prototype-linked to Constructor.prototype.
  // Object.create does link-at-birth — no mutation, no __proto__.
  const instance = Object.create(Constructor.prototype);
 
  // Step 2: run the constructor with `this` = the new object
  const result = Constructor.apply(instance, args);
 
  // Steps 3–4: object return overrides; primitive return is discarded
  const isObject =
    result !== null && (typeof result === "object" || typeof result === "function");
  return isObject ? result : instance;
}

Verified behavior

function Person(name, age) {
  this.name = name;
  this.age = age;
}
Person.prototype.greet = function () {
  return `Hi, I'm ${this.name}`;
};
 
const p = myNew(Person, "Ada", 36);
p.name;                 // "Ada"
p.greet();              // "Hi, I'm Ada"  — prototype methods work (step 1)
p instanceof Person;    // true            — the chain is linked
 
// The return-override rule:
function ReturnsObject() { this.a = 1; return { b: 2 }; }
myNew(ReturnsObject);   // { b: 2 } — object return WINS; this.a is lost
 
function ReturnsPrimitive() { this.a = 1; return 42; }
myNew(ReturnsPrimitive); // { a: 1 } — primitive return silently IGNORED

That last pair is the discriminating test. Most candidates know steps 1–2; steps 3–4 are the "did you read the spec or a blog post" line — and they're not trivia: the return-override rule is why factory-style constructors and singleton patterns work with new at all, and why a stray return in a constructor produces one of JavaScript's most confusing bugs (half your fields vanish, no error anywhere).

Why each step matters

  • Object.create(Constructor.prototype), not {} + mutation — setting __proto__ after creation deoptimizes in engines and reads as not knowing Object.create exists. Link at birth.
  • .apply(instance, args) — this is the one context where this is a brand-new object; connect it to the binding rules: new binding beats every other rule, including an explicit bind.
  • The object-or-function check — functions count as objects for the override (returning a function from a constructor replaces the instance). The !== null guard matters because typeof null === "object" (the famous bug).

Edge cases interviewers probe

  • Arrow functionsmyNew(() => {}) should throw like real new does. Our version throws, but later and differently: arrows have no prototype, so Object.create(undefined) throws a TypeError about the prototype argument. Real new rejects at the [[Construct]] check. If asked to match exactly, check Constructor.prototype existence up front — and note that this heuristic can't distinguish arrows from methods ({ m() {} } also lacks prototype), which real new handles via an internal slot userland can't see. Naming that limit honestly is the senior answer.
  • class constructorsmyNew(SomeClass) throws: classes are only callable via [[Construct]] (Class constructor cannot be invoked without 'new'). Your apply is a plain call, so this fails — correctly demonstrating that classes are not just sugar over functions in every respect. Great volunteered detail.
  • new.target — inside a real new, new.target is the constructor; inside myNew it's undefined, because only [[Construct]] sets it. This is exactly why bind's modern constructor-detection uses new.target — and why your polyfill can't fool it. Two honest gaps (this and classes) beat one pretend-perfect answer.
  • Constructor throws — the half-initialized instance is simply garbage-collected; no cleanup needed, worth one sentence.
  • Reflect.construct(Constructor, args) — the real userland new: handles classes, sets new.target, even allows a different newTarget third argument. "My myNew is the teaching version; Reflect.construct is the production version" closes the question.

Common mistakes

  • const instance = {}; instance.__proto__ = C.prototype — works, slow, and signals 2014.
  • Forgetting the return-override rule entirely, or getting it backwards (honoring primitives, ignoring objects).
  • typeof result === "object" without the null check.
  • Claiming classes work with a plain-call implementation.
  • Not connecting to instanceof — the prototype link made in step 1 is exactly what instanceof walks; the two questions are one mechanism viewed from both ends.

Follow-up questions

  • "Now implement instanceof." — the standard pairing: custom instanceof checks the link your step 1 created.
  • "Why does new Date() differ from Date()?" — many built-ins branch on new.target (or its legacy equivalent): Date() without new returns a string. Constructor-vs-call dual behavior is spec-sanctioned and your myNew can't reproduce it for built-ins — tie back to [[Construct]].
  • "What does new cost vs Object.create + init function?" — essentially nothing different in modern engines (hidden classes form either way); the real answer is API semantics, not performance.
  • "How does bind's constructor case interact?"new (fn.bind(a))() ignores the bound this — because new binding wins; the full mechanics live in custom bind.
  • "What's the factory-function alternative and when is it better?" — plain functions returning objects: no this pitfalls, trivially composable, but no shared prototype methods without extra wiring and no instanceof. The trade-off discussion is prototypal inheritance's closing argument.

  • Custom instanceof

    intermediate

    Walk the prototype chain by hand, then meet Symbol.hasInstance — and learn where instanceof lies to you.

  • Implement bind including partial application and the new-operator edge case most candidates miss.

  • Object.assign

    intermediate

    Copy enumerable own properties across sources, with getter evaluation and null-target errors.

  • Same trick as call but with an arguments array — plus the subtle differences that interviewers probe.