The new Operator & Constructors Interview Questions & Answers
25 questions Updated 2026-06-18
JavaScript constructor interview questions — what the new operator does, constructor functions, returning values, new.target, and common new-related bugs.
Read the in-depth guideThe new Operator & Constructor Functions in JavaScript — How Object Creation Really Worksnew Fn(args) performs four steps:
- Creates a fresh empty object.
- Sets that object's prototype to
Fn.prototype. - Calls
Fnwiththisbound to the new object. - Returns the new object — unless
Fnexplicitly returns its own object.
function User(name) { this.name = name }
const u = new User('Ada')
// roughly:
// const u = Object.create(User.prototype)
// User.call(u, 'Ada')
Those four steps are the whole "magic" of new. Understanding them explains
this, the prototype link, and the return-value rules below.
A regular function intended to be called with new to create objects. By
convention its name is capitalized. Inside, this is the new instance.
function Point(x, y) {
this.x = x
this.y = y
}
const p = new Point(1, 2) // { x: 1, y: 2 }
There's nothing special about the function itself — any function can be a
constructor. The capitalization is a signal to callers that it must be invoked
with new. Classes are the modern replacement.
Without new, the function runs as a plain call: this is undefined (strict)
or the global object (sloppy), so it doesn't create an instance — and in sloppy
mode it leaks properties onto the global object.
function User(name) { this.name = name }
const u = User('Ada') // no new
u // undefined (function returns nothing)
// sloppy mode: created a global `name`!
Guards: use a class (which throws if called without new), or check
new.target/instanceof and re-invoke with new. This bug is exactly why
classes enforce new.
If a constructor returns an object, that object replaces the newly created
this. If it returns a primitive (or nothing), the primitive is ignored and
this is returned as usual.
function A() { this.x = 1; return { y: 2 } }
new A() // { y: 2 } — object return overrides `this`
function B() { this.x = 1; return 42 }
new B() // { x: 1 } — primitive return ignored
This "return an object to override" behavior enables some factory tricks but is usually a footgun — most constructors return nothing.
A meta-property that is the constructor invoked with new, or undefined for a
normal call. It lets a function detect how it was called.
function User(name) {
if (!new.target) throw new Error('User must be called with new')
this.name = name
}
User('Ada') // throws
new User('Ada') //
In a class hierarchy, new.target is the actual subclass being constructed —
useful for abstract base classes and for factory logic that varies by concrete
type.
new Fn() sets the instance's prototype to Fn.prototype, so the instance
inherits everything defined there.
function Dog() {}
Dog.prototype.bark = () => 'woof'
const d = new Dog()
Object.getPrototypeOf(d) === Dog.prototype // true
d.bark() // 'woof' (inherited)
That's why methods go on Fn.prototype — every new instance shares them via
the prototype link. Reassigning Fn.prototype after creating an instance
doesn't change that instance's prototype.
- A constructor is used with
new, relies onthis, and links instances to a shared prototype. - A factory is a normal function that returns an object (no
new, nothis), often using closures for privacy and composition for behavior.
function User(name) { this.name = name } // constructor -> new User()
function createUser(name) { return { name } } // factory -> createUser()
Factories avoid new/this pitfalls and offer closure-based privacy;
constructors/classes share methods via the prototype (more memory-efficient for
many instances). Both are valid.
When called with new, this is the brand-new instance being created, so
assignments to this.x become the object's own properties.
function Account(balance) {
this.balance = balance // own property of the new instance
this.deposit = function (n) { this.balance += n }
}
Called without new, this follows normal rules (undefined/global), which
is the source of the "forgot new" bug. Inside a class constructor, this is the
instance (and is undefined until super() runs in a subclass).
Use instance.constructor (inherited from the prototype) or instanceof.
function User() {}
const u = new User()
u.constructor === User // true
u.constructor.name // 'User'
u instanceof User // true
constructor is informational and can be wrong if the prototype was replaced
without restoring it. For reliable type checks, prefer instanceof (or
duck-typing), not the constructor property.
JavaScript provides constructors for its core types: Object, Array,
Function, Date, RegExp, Map, Set, Promise, Error, and the wrapper
constructors String/Number/Boolean.
new Date()
new Map()
new Error('boom')
new Array(3) // [ <3 empty items> ] — quirky
Most are used with new, though some (like Array, Object) also work without
it. Avoid new String/Number/Boolean — they create wrapper objects that
break === and are always truthy.
new Array(n) with a single number creates an empty array of length n
(holes), not an array containing n. With multiple args it creates an array of
those elements.
new Array(3) // [ <3 empty items> ] — length 3, no values
new Array(1, 2, 3) // [1, 2, 3]
Array.of(3) // [3] — the unambiguous alternative
Array.from({length: 3}, (_, i) => i) // [0, 1, 2]
This inconsistency is why Array.of exists and why array literals [] are
preferred. The "empty items" are holes, which behave oddly with iteration.
obj instanceof Ctor is true when Ctor.prototype is in obj's prototype
chain — i.e. obj was (effectively) built by Ctor or a subclass.
const d = new Date()
d instanceof Date // true
d instanceof Object // true (Object.prototype is in the chain)
It checks the prototype chain, not the literal constructor used, so it sees
inheritance. It can be fooled by reassigned prototypes and fails across realms —
use Array.isArray / Object.prototype.toString for those cases.
In a derived class, the instance (this) is created by the parent
constructor, so you must call super() first to initialize it. Accessing this
before super() throws a ReferenceError.
class Animal { constructor(name) { this.name = name } }
class Dog extends Animal {
constructor(name) {
this.bark = true // ReferenceError — before super()
super(name)
this.bark = true //
}
}
This is a real difference from constructor functions, where this exists
immediately. The rule guarantees the parent has set up the object before the
child touches it.
Cache the instance and return it on subsequent new calls — leveraging the
"return an object overrides this" rule.
class Config {
constructor() {
if (Config.instance) return Config.instance
Config.instance = this
}
}
new Config() === new Config() // true — same instance
Because returning an object from a constructor replaces the new this, the
second new gets the cached instance. (A module-level singleton or a frozen
object is often simpler.)
Put methods on the prototype (shared, one copy); put per-instance data
in the constructor (on this).
function User(name) {
this.name = name // instance data
}
User.prototype.greet = function () { // shared method
return `Hi, ${this.name}`
}
Defining methods inside the constructor (this.greet = function(){}) creates a
new function per instance — wasteful. Classes handle this correctly by placing
methods on the prototype automatically.
new overrides a bound this — the instance wins. Bound arguments are
still applied.
function Point(x, y) { this.x = x; this.y = y }
const Bound = Point.bind({ ignored: true }, 10) // pre-bind x = 10
const p = new Bound(20)
p.x // 10 (bound arg kept)
p.y // 20
// the bound `this` ({ ignored: true }) is ignored — p is a fresh Point
This reflects precedence: new beats explicit binding. It's the one case where
bind's "permanent" this doesn't hold.
Function constructors are hoisted (usable before their line); class constructors are hoisted but in the temporal dead zone, so they're not usable early.
new Fn() // works — function declaration hoisted
function Fn() {}
new Cls() // ReferenceError — TDZ
class Cls {}
So switching from a constructor function to a class can break code that instantiated it above its declaration. Always declare classes before use.
Check new.target against the base and throw, so only subclasses can be
created.
class Shape {
constructor() {
if (new.target === Shape) throw new Error('Shape is abstract')
}
area() { throw new Error('not implemented') }
}
class Circle extends Shape { area() { return 1 } }
new Shape() // throws
new Circle() //
JavaScript has no built-in abstract keyword, so new.target (plus throwing
from unimplemented methods) is the idiom for abstract base classes.
The functional form of new — it invokes a constructor and optionally lets you
specify a different prototype (newTarget), useful in metaprogramming and for
subclassing built-ins.
const obj = Reflect.construct(Array, [1, 2, 3]) // like new Array(1,2,3)
// third arg sets new.target / the prototype to use:
Reflect.construct(Base, args, Derived)
It's how you invoke a constructor dynamically (with an args array) and control the resulting prototype — the building block frameworks use for advanced instantiation.
Call the parent with Parent.call(this, args) so the parent initializes the
child's instance fields.
function Animal(name) { this.name = name }
function Dog(name, breed) {
Animal.call(this, name) // parent sets this.name
this.breed = breed
}
This is the pre-class way to "chain" constructors. With classes, super(name)
does the same thing, and is required before using this in a subclass.
'function' — both constructor functions and classes are functions under the
hood.
function Fn() {}
class Cls {}
typeof Fn // 'function'
typeof Cls // 'function'
Classes are special functions (strict mode, non-enumerable methods, must be
called with new), but typeof doesn't distinguish them. To tell a class from
a regular function, you'd inspect the source or try calling without new.
Keep constructors focused on initialization. Heavy work — network calls, DOM access, throwing on async failures — makes objects hard to create, test, and reason about.
// side-effectful constructor
class User { constructor(id) { this.data = fetch(`/u/${id}`) } }
// construct simply, do work in a method/static factory
class User {
constructor(data) { this.data = data }
static async load(id) { return new User(await (await fetch(`/u/${id}`)).json()) }
}
A common pattern is a static async factory (User.load) that does the I/O
and then constructs a plain instance — constructors can't be async.
{} is shorter, clearer, and slightly faster than new Object(), with no
downside. The same goes for [] over new Array() and /regex/ over
new RegExp() for static patterns.
const a = {} // idiomatic
const b = new Object() // verbose, no benefit
Use new for types that genuinely need a constructor (Date, Map, Set,
Promise, custom classes), and literals for the primitives-with-literals
(objects, arrays, regexes).
Have each method return this, so calls can be chained on the instance.
class QueryBuilder {
constructor() { this.parts = [] }
where(c) { this.parts.push(c); return this }
order(o) { this.parts.push(o); return this }
build() { return this.parts.join(' ') }
}
new QueryBuilder().where('a=1').order('b').build()
Returning this (the instance) from methods is the builder/fluent pattern. It
works because methods are called on the instance, so this is consistent across
the chain.
Use closures: declare variables inside the constructor and expose only methods that close over them — the variables aren't accessible as properties.
function Counter() {
let count = 0 // private
this.increment = () => ++count
this.value = () => count
}
const c = new Counter()
c.increment()
c.value() // 1
c.count // undefined — truly private
The trade-off: these methods are per-instance (closures), not shared on the
prototype. Modern classes offer #private fields for shared-method privacy
without closures.
Practice tests are coming soon
Get notified when interactive mock interviews and quizzes launch.