skip to content

In JavaScript, what does an object's `constructor` property refer to, and is it an own property of the instance?

level: juniorimportance: should knowfreq 55%

answer

  1. not stored on the instance
  2. lives one hop up the chain
  3. circular link between function and prototype
  4. hidden from for...in by default
  5. writable, so it can lie

basics

~10 s

The constructor property is inherited, not own. It sits on the function's prototype object as a non-enumerable property pointing back at that function, and instances reach it by walking the prototype chain.

solid answer

~40 s

When you declare `function Dog() {}`, the engine also creates `Dog.prototype`, and that object gets a `constructor` property pointing back at `Dog`. It is non-enumerable, writable and configurable. An instance made with `new Dog()` has its internal prototype set to `Dog.prototype`, so `d.constructor` is an ordinary property lookup that misses on the instance and finds the one on the prototype — `d.hasOwnProperty('constructor')` is `false`. Because it is non-enumerable it never shows up in `for...in` or `Object.keys`. The important caveat is that nothing in the language keeps it honest: it is a plain writable property, so any code can overwrite it or shadow it on the instance, and replacing `Dog.prototype` with a fresh object drops it entirely. Treat it as a convention, not as a trustworthy type tag.

code

javascript · 12 lines
javascript
function Dog(name) { this.name = name; }
const d = new Dog('Rex');

console.log(d.hasOwnProperty('constructor'));               // false
console.log(Object.getPrototypeOf(d) === Dog.prototype);    // true
console.log(Dog.prototype.constructor === Dog);             // true
console.log(d.constructor === Dog);                         // true

console.log(Object.getOwnPropertyDescriptor(Dog.prototype, 'constructor'));
// { value: [Function: Dog], writable: true, enumerable: false, configurable: true }

for (const key in d) console.log(key);                      // "name" only

go deeper

for a junior

Be able to say that constructor points back at the function that created the object, that it lives on that function's prototype object, and that the instance only inherits it.

for a middle

Explain the lookup path and the property attributes: non-enumerable, writable, configurable, which is why for...in skips it and why any code can overwrite it.

for a senior

Show why you never branch on constructor in production code — it is trivially shadowed, lost on prototype reassignment, and undefined for null-prototype objects and cross-realm values.

for a principal

Frame the tradeoff: JavaScript exposes its object graph as ordinary mutable properties, which buys unmatched flexibility but means identity metadata is advisory. Decide where your codebase carries explicit, non-forgeable tags instead.

## What gets created when you declare a function Every ordinary function declaration or function expression in JavaScript is born with a `prototype` property whose value is a fresh plain object. That object is not the function's own prototype — it is the object that will become the internal prototype of anything created with `new` on that function. The engine puts exactly one property on it for you: ```js function Dog(name) { this.name = name; } Object.getOwnPropertyNames(Dog.prototype); // ["constructor"] Object.getOwnPropertyDescriptor(Dog.prototype, 'constructor'); // { value: Dog, writable: true, enumerable: false, configurable: true } ``` So `Dog.prototype.constructor === Dog`. The link is circular by design: from the function you can reach its prototype object, and from that object you can get back to the function. ## How an instance reaches it `new Dog('Rex')` creates a fresh object whose internal prototype (the `[[Prototype]]` slot, readable with `Object.getPrototypeOf`) is set to `Dog.prototype`. Reading `d.constructor` therefore runs the normal property lookup: the engine looks on `d` itself, finds nothing, follows the chain to `Dog.prototype`, and finds `constructor` there. ```js const d = new Dog('Rex'); d.hasOwnProperty('constructor'); // false — it is inherited Object.getPrototypeOf(d) === Dog.prototype; // true d.constructor === Dog; // true ``` The consequence people miss in interviews is that `constructor` is not a per-object field written at creation time. Nothing is stamped onto the instance. If you later mutate `Dog.prototype.constructor`, every existing instance immediately reports the new value, because they were all reading through the chain the whole time. ## Non-enumerable matters The default `constructor` is non-enumerable, which is why iterating an instance behaves the way you expect: ```js for (const key in d) console.log(key); // "name" only Object.keys(Dog.prototype); // [] ``` `for...in` visits enumerable string-keyed properties along the whole prototype chain, so if `constructor` were enumerable it would show up in every such loop. This detail becomes practical the moment someone re-creates the link by plain assignment: `Dog.prototype.constructor = Dog` defines an enumerable data property, and now `for...in` over an instance yields `constructor` as well. ## Classes work the same way `class` syntax does not invent a new mechanism here. A class declaration still produces a function whose `prototype` object carries `constructor` pointing back at the class: ```js class Cat {} Cat.prototype.constructor === Cat; // true new Cat().constructor === Cat; // true ``` The difference is in the attributes of the surrounding machinery: for a class, the `prototype` property of the class itself is non-writable and non-configurable, so you cannot swap the prototype object out the way you can for a plain function. The `constructor` property on the prototype object is still writable, though — classes do not make it tamper-proof, they only make it harder to lose by accident. ## Where it does not exist Not every function has a prototype object to hang `constructor` on. Arrow functions, concise object methods, and getters/setters have no `prototype` property at all, because they cannot be constructed. Bound functions likewise have no own `prototype`. ```js const arrow = () => {}; arrow.prototype; // undefined Object.create(null).constructor; // undefined — no chain to inherit from ``` An object created with `Object.create(null)` has no prototype at all, so `constructor` is simply `undefined` rather than pointing anywhere. ## Why it is not a reliable type tag Because `constructor` is an ordinary writable property, three things can make it lie: 1. Someone shadows it on the instance: `d.constructor = Something` creates an own property that wins the lookup. 2. Someone overwrites it on the prototype: `Dog.prototype.constructor = Cat`. 3. Someone replaces the prototype object wholesale: `Dog.prototype = { bark() {} }` — the new object has no `constructor` of its own, so instances made afterwards inherit `Object.prototype.constructor`, which is `Object`. The third case is the one that actually shows up in real code, in hand-written prototypal inheritance. For asking "what made this object", the language offers stronger tools than reading `constructor`; use it for its intended purpose — a convenient back-reference from a prototype object to its function — and treat any value you read out of it as advisory.

  • If `constructor` is inherited rather than own, what happens to existing instances when you overwrite `Dog.prototype.constructor` after they were created?
    They all report the new value immediately. Nothing was copied onto the instances at construction time — each read of `d.constructor` is a fresh lookup that misses on the instance and lands on `Dog.prototype`. That is also why one careless assignment on a shared prototype affects every object already in memory.
  • What does `Object.create(null).constructor` evaluate to, and why?
    `undefined`. `Object.create(null)` produces an object with no prototype at all, so the lookup for `constructor` has nowhere to continue after missing on the object itself. Such objects also lack `hasOwnProperty` and `toString` for the same reason, which is exactly why they are popular as pure dictionaries.
  • Does an arrow function have a `prototype.constructor` pair?
    No. Arrow functions, concise methods and accessors have no `prototype` property at all, because they lack the internal construct behaviour that `new` requires. There is no prototype object, so there is nothing for a `constructor` back-reference to live on.

saying these in an interview costs you the question

  • Saying constructor is stored on each instance
  • Calling obj.constructor a reliable runtime type check
  • Claiming constructor shows up in Object.keys(instance)
  • Assuming plain assignment keeps it non-enumerable
  • Thinking arrow functions have a prototype object

context