What it does: Constructor functions have a prototype property that becomes the [[Prototype]] of objects created with new.

How Constructor Prototypes Work:

Basic Setup:

	function Rabbit(name) {
	  this.name = name;
	}
	
	Rabbit.prototype = { eats: true }; // This becomes [[Prototype]] for new objects
	
	let rabbit1 = new Rabbit("White");
	let rabbit2 = new Rabbit("Black");
	
	console.log(rabbit1.eats); // true (inherited)
	console.log(rabbit2.eats); // true (inherited)

The Assignment Happens at new Time:

function Dog(name) { this.name = name; }
 
Dog.prototype = { barks: true };
let dog1 = new Dog("Rex");
 
Dog.prototype = { meows: true }; // Change after creation
let dog2 = new Dog("Fluffy");
 
console.log(dog1.barks); // true (keeps old prototype)
console.log(dog2.barks); // undefined (has new prototype)
console.log(dog2.meows); // true

Default F.prototype Behavior:

Every Function Gets Default Prototype:

javascript

function MyFunc() {}
// JavaScript automatically creates:
// MyFunc.prototype = { constructor: MyFunc };
 
console.log(MyFunc.prototype.constructor === MyFunc); // true

Constructor Property in Action:

javascript

function Car(model) { this.model = model; }
 
let car1 = new Car("Toyota");
let car2 = new car1.constructor("Honda"); // Same as: new Car("Honda")
 
console.log(car2.model); // "Honda"

The Simple Rules:

F.prototype Assignment: Only matters when you call new F() Default Prototype: Always { constructor: F } Constructor Property: Points back to the original function

javascript

function Animal() {}
let animal = new Animal();
 
console.log(animal.constructor === Animal); // true
console.log(animal.__proto__ === Animal.prototype); // true

Interview Gotchas:

javascript

// Gotcha 1: Overwriting prototype loses constructor
function Bird() {}
Bird.prototype = { flies: true };
 
let bird = new Bird();
console.log(bird.constructor === Bird); // false! (lost constructor)
 
// Fix: Keep constructor when overwriting
Bird.prototype = {
  flies: true,
  constructor: Bird  // Manually preserve it
};
 
// Gotcha 2: prototype property vs [[Prototype]]
function Fish() {}
let fish = new Fish();
 
Fish.prototype.swims = true; // This is F.prototype (constructor's property)
console.log(fish.__proto__.swims); // true (fish's [[Prototype]])
 
// Gotcha 3: Regular objects don't care about prototype property
let obj = { prototype: "whatever" };
// This does NOTHING special - just a regular property

What Exists in This System:

  • F.prototype - constructor function’s property (not [[Prototype]])
  • constructor - property that points back to the constructor
  • Default prototype - { constructor: F } created automatically
  • new operator - uses F.prototype to set [[Prototype]]

Never Get Tricked - Follow These Rules:

  1. F.prototype ≠ Prototype - first is a property, second is internal link
  2. Assignment happens at new time - changing F.prototype later doesn’t affect existing objects
  3. Constructor can be lost - if you overwrite prototype, add constructor back manually
  4. Only works with new - regular function calls ignore F.prototype completely

Bottom line: Constructor functions use their prototype property as a template for what new objects should inherit from, but only when called with new.


What .prototype actually is:

function Dog(name) { this.name = name; }
// Dog.prototype is just a PROPERTY on the Dog function
// You can put anything in it: objects, functions, variables
Dog.prototype = { barks: true, tail: "wagging" };
Dog.prototype.run = function() { return "running"; };

The Memory Address Connection:

function Dog(name) { this.name = name; }
Dog.prototype = { barks: true }; // This object lives at memory address X100
 
let dog1 = new Dog("Rex");
// dog1[[Prototype]] → points to X100 (the memory address)
// NOT to "Dog.prototype" as a concept, but to the ACTUAL OBJECT at X100
 
Dog.prototype = { meows: true }; // NEW object at memory address X200
// But dog1[[Prototype]] still points to X100! It's LOCKED IN.
 
let dog2 = new Dog("Fluffy");
// dog2[[Prototype]] → points to X200 (the new object)

Visual Memory Layout:

Memory X100: { barks: true }     ← dog1[[Prototype]] points here FOREVER
Memory X200: { meows: true }     ← dog2[[Prototype]] points here

Dog.prototype → now points to X200 (but dog1 doesn't care!)

Why the behavior happens:

console.log(dog1.barks); // true
// dog1 looks at its [[Prototype]] → goes to X100 → finds { barks: true }
 
console.log(dog1.meows); // undefined  
// dog1 looks at its [[Prototype]] → goes to X100 → no meows property
 
console.log(dog2.barks); // undefined
// dog2 looks at its [[Prototype]] → goes to X200 → no barks property
 
console.log(dog2.meows); // true
// dog2 looks at its [[Prototype]] → goes to X200 → finds { meows: true }

The Key Rule:

  • At new time: object[[Prototype]] gets the MEMORY ADDRESS of whatever F.prototype points to
  • After creation: object[[Prototype]] is LOCKED to that memory address
  • Changing F.prototype later creates a NEW memory address, but existing objects don’t care

Bottom line: It’s about memory addresses, not dynamic lookups. Once created, your object’s [[Prototype]] is glued to a specific memory location forever.