F.prototype
You already know two things separately: objects can inherit from other objects through a hidden [[Prototype]] link, and you can build objects with a constructor function called through new F(). This chapter connects them. The bridge is a property named prototype that lives on the constructor function.
The rule in one sentence: when F.prototype holds an object, the new operator copies that object into the [[Prototype]] of whatever it creates.
Two different things that share a name
The naming here trips people up, so pin it down before anything else. F.prototype is an ordinary property named "prototype" sitting on the function object F. It is a normal property you can read and reassign. It just happens to be spelled the same as the concept “prototype”.
That is a separate thing from [[Prototype]], the hidden internal slot that makes inheritance work. One is a public property on a function; the other is an internal link on an object. The new operator is what ties them together.
Here is the mechanism in code:
let machine = {
powered: true
};
function Drone(name) {
this.name = name;
}
Drone.prototype = machine;
let drone = new Drone("Skylark"); // drone.__proto__ == machine
alert( drone.powered ); // true
Assigning Drone.prototype = machine sets up a standing instruction: “every time someone runs new Drone, wire the new object’s [[Prototype]] to machine.” So drone never got a powered property of its own, yet reading drone.powered finds true by walking up the chain to machine.
Try it live. The drone has only its own name, but it reads powered straight off the shared machine prototype:
The diagram below shows the two relationships. The prototype link is horizontal, because it is just a property pointing from the function to the object. The [[Prototype]] link is vertical, because it is the inheritance edge from drone up to machine.
To make that timing concrete, watch what happens when the property changes between two new calls:
function Drone() {}
Drone.prototype = { rotors: 4 };
let first = new Drone(); // [[Prototype]] -> { rotors: 4 }
Drone.prototype = { rotors: 2 }; // swap the property
let second = new Drone(); // [[Prototype]] -> { rotors: 2 }
alert(first.rotors); // 4 (unchanged — kept its original prototype)
alert(second.rotors); // 2
first did not follow the reassignment. Each object froze the link it was handed at construction.
Build a drone, then swap the prototype, then build another. The first one keeps the value it was born with while only newer drones see the change:
The default F.prototype and the constructor property
Here is a detail that is easy to miss: every function comes with a prototype property already, even one you never touched.
By default that property is an object holding a single property, constructor, which points right back at the function itself.
function Drone() {}
/* the default prototype, roughly:
Drone.prototype = { constructor: Drone };
*/
You can verify the loop directly:
function Drone() {}
// by default:
// Drone.prototype = { constructor: Drone }
alert( Drone.prototype.constructor == Drone ); // true
Since constructor lives on the prototype object, and every drone inherits from that object, each drone sees constructor too — through the [[Prototype]] chain, not as its own property:
function Drone() {}
// by default:
// Drone.prototype = { constructor: Drone }
let drone = new Drone(); // inherits from {constructor: Drone}
alert(drone.constructor == Drone); // true (from prototype)
Cloning “the same kind” of object
One practical payoff: constructor lets you build another object of the same type without naming the constructor yourself.
function Drone(name) {
this.name = name;
alert(name);
}
let drone = new Drone("Skylark");
let drone2 = new drone.constructor("Nightjar");
This shines when you hold an object but do not know which function produced it — say it arrived from a third-party library — and you need one more of the same shape. Reach through .constructor and call new on it.
JavaScript does not police the constructor value
Now the sharp edge. That tidy back-pointer is convenient, but the language makes no promise to keep it correct.
JavaScript only sets up constructor once, in the default prototype. From there, it is entirely your responsibility.
Nothing re-checks it, nothing repairs it. The most common way to lose it is replacing the whole prototype object. When you assign a brand-new object to F.prototype, that object has no constructor property, so the link is gone:
function Drone() {}
Drone.prototype = {
hovers: true
};
let drone = new Drone();
alert(drone.constructor === Drone); // false
drone.constructor now resolves further up the chain — to Object — because the replacement prototype never carried its own constructor, and neither does drone.
Two ways to keep it correct
Option 1 — extend instead of replace. Add properties to the existing prototype rather than swapping the whole object. The default constructor stays put.
function Drone() {}
// Don't overwrite Drone.prototype wholesale —
// just add to it
Drone.prototype.hovers = true
// the default Drone.prototype.constructor is preserved
Option 2 — put constructor back yourself. If you do want to assign a fresh object, restore the property by hand.
Drone.prototype = {
hovers: true,
constructor: Drone
};
// now constructor is correct again, because we set it
See all three side by side. Pick how Drone.prototype is built and watch whether drone.constructor still points back at Drone:
Summary
This chapter covered how a constructor function decides the [[Prototype]] of the objects it builds. Later patterns for organizing code build directly on this mechanism.
- The
F.prototypeproperty — not to be confused with the internal[[Prototype]]— becomes the[[Prototype]]of new objects whennew F()runs. F.prototypemust be an object ornull. Any other value is ignored for this purpose.- The property has this special effect only on a constructor function called with
new.
On a regular object, a property named prototype carries no special meaning at all:
let user = {
name: "Maya",
prototype: "Bla-bla" // no magic at all
};
By default every function starts with F.prototype = { constructor: F }, which is why you can usually recover the constructor of an object through its constructor property — as long as nobody threw that default away.