JavaScript __proto__ vs prototype: What Is the Difference?

Understand the fundamental difference between __proto__ and prototype in JavaScript. Learn what each one is, when each exists, how they connect via the new operator, and why mixing them up causes subtle bugs.

JavaScriptintermediate
11 min read

__proto__ and prototype are two of the most consistently confused concepts in JavaScript. They sound similar and relate to the same prototype chain mechanism, but they exist on different types of objects, serve different purposes, and are accessed differently. This guide separates them completely with clear definitions, diagrams, and code examples.

The One-Line Definitions

  • prototype — A property on function objects (specifically constructor functions and classes). It is the object that will become the [[Prototype]] of any instance created by calling that function with new.
  • __proto__ — The actual [[Prototype]] link on any object instance. It points to the prototype object the instance currently inherits from.

Put simply:

  • prototype is the blueprint provider — it lives on the constructor
  • __proto__ is the live link — it lives on the instance, pointing up the chain

Visualizing the Relationship

texttext
         [Function: Animal]
              |
    Animal.prototype ← (this is where methods live)
         /        \
  [dog instance]  [cat instance]
  dog.__proto__ ──────────────┘
  cat.__proto__ ──────────────┘

When you call new Animal(), the new object's __proto__ is set to Animal.prototype. Both dog and cat share the same prototype object — they copy nothing, they link.

Code Demonstration

javascriptjavascript
function Animal(name) {
  this.name = name;   // Own property — on the instance
}
 
// Adding to Animal.prototype — available to all instances via chain
Animal.prototype.breathe = function () {
  return `${this.name} breathes`;
};
 
const dog = new Animal("Rex");
const cat = new Animal("Whiskers");
 
// PROTOTYPE: the property on the constructor function
console.log(typeof Animal.prototype);          // "object"
console.log(Animal.prototype.breathe);        // [Function: breathe]
 
// __proto__: the link on each instance
console.log(dog.__proto__ === Animal.prototype); // true — same object!
console.log(cat.__proto__ === Animal.prototype); // true — same object, shared!
 
// They point to the SAME prototype object:
console.log(dog.__proto__ === cat.__proto__);  // true
 
// Modifying Animal.prototype affects ALL instances:
Animal.prototype.speak = function () {
  return `${this.name} makes a sound`;
};
console.log(dog.speak()); // "Rex makes a sound" — picked up immediately
console.log(cat.speak()); // "Whiskers makes a sound"

What the new Operator Does

Understanding new clarifies the relationship:

javascriptjavascript
function Animal(name) {
  this.name = name;
}
Animal.prototype.breathe = function () { return "breathing"; };
 
// new Animal("Rex") does this, roughly:
function simulateNew(Constructor, ...args) {
  // 1. Create a new object
  const instance = {};
 
  // 2. Set its [[Prototype]] to Constructor.prototype
  Object.setPrototypeOf(instance, Constructor.prototype);
  // This is what makes instance.__proto__ === Animal.prototype
 
  // 3. Call the constructor with this = instance
  const result = Constructor.apply(instance, args);
 
  // 4. Return the instance (or the constructor's return value if it's an object)
  return (typeof result === "object" && result !== null) ? result : instance;
}
 
const dog = simulateNew(Animal, "Rex");
console.log(dog.breathe()); // "breathing" — works exactly like new Animal

prototype Only Exists on Functions

Plain objects and instances do not have a prototype property (unless you add one manually):

javascriptjavascript
const obj = {};
console.log(obj.prototype);       // undefined — plain objects don't have .prototype
 
function fn() {}
console.log(fn.prototype);        // {} — functions DO have .prototype
 
const arr = [1, 2, 3];
console.log(arr.prototype);       // undefined — arrays are instances, not constructors
console.log(Array.prototype);     // [...] — Array is a constructor function

proto — Deprecated but Still Widely Used

__proto__ is technically deprecated. The preferred API is:

OperationLegacy (proto)Modern API
Get prototype of objobj.__proto__Object.getPrototypeOf(obj)
Set prototype of objobj.__proto__ = protoObject.setPrototypeOf(obj, proto)
Create with prototypeN/AObject.create(proto)
javascriptjavascript
const proto = { greet() { return "hello"; } };
const obj = Object.create(proto);
 
console.log(Object.getPrototypeOf(obj) === proto); // true
console.log(obj.__proto__ === proto);               // true — same thing, different syntax

__proto__ is standardized in Annex B of the spec (for web compatibility) and works everywhere, but Object.getPrototypeOf is the preferred way to read it.

The constructor Property

Every Function.prototype object also has a constructor property pointing back to the function:

javascriptjavascript
function Dog(name) { this.name = name; }
 
console.log(Dog.prototype.constructor === Dog); // true — points back to Dog
console.log(Dog.prototype.constructor.name);    // "Dog"
 
const rex = new Dog("Rex");
console.log(rex.constructor === Dog);           // true — via prototype chain
// rex doesn't have own property "constructor", inherits from Dog.prototype

This constructor property can break if you replace prototype entirely:

javascriptjavascript
// Replacing prototype breaks constructor
Dog.prototype = {
  bark() { return "woof"; },
};
// Dog.prototype.constructor is now Object (the {} literal's constructor)
 
const rex = new Dog("Rex");
console.log(rex.constructor === Dog);    // false — broken!
console.log(rex.constructor === Object); // true — wrong!
 
// Fix: restore constructor
Dog.prototype = {
  constructor: Dog, // Manually restore
  bark() { return "woof"; },
};

class Syntax and the Same Relationship

class syntax creates the exact same prototype/__proto__ relationships:

javascriptjavascript
class Animal {
  constructor(name) { this.name = name; }
  breathe() { return `${this.name} breathes`; }
}
 
class Dog extends Animal {
  bark() { return `${this.name} barks`; }
}
 
const rex = new Dog("Rex");
 
// Same structure as function constructors:
console.log(rex.__proto__ === Dog.prototype);             // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true
 
// Methods live on the prototype objects:
console.log(Dog.prototype.hasOwnProperty("bark"));    // true
console.log(Animal.prototype.hasOwnProperty("breathe")); // true
console.log(rex.hasOwnProperty("bark"));              // false — it's on Dog.prototype

Quick Reference Diagram

texttext
new Dog("Rex")
        |
        v
    rex (instance)
        |
    rex.__proto__ ────────────→ Dog.prototype
                                      |
                              Dog.prototype.__proto__ ──────→ Animal.prototype
                                                                     |
                                                          Animal.prototype.__proto__ ──→ Object.prototype
                                                                                                  |
                                                                                       Object.prototype.__proto__ → null
Rune AI

Rune AI

Key Insights

  • prototype lives on constructor functions: It is the object assigned as [[Prototype]] to each instance created with new — it is NOT a property that instances have (usually)
  • proto lives on instances: It is the actual [[Prototype]] link pointing to the object that was the constructor's prototype at creation time
  • new connects them: The first thing new does is create an object with [[Prototype]] = Constructor.prototype, establishing the inheritance link
  • Both point to the same object: dog.proto === Animal.prototype is always true for correctly-constructed instances — they are not copies, they are the same reference
  • Use Object.getPrototypeOf for modern code: proto works everywhere but is deprecated as a direct property access pattern; Object.getPrototypeOf(obj) is the standardized API
RunePowered by Rune AI

Frequently Asked Questions

Can I safely use __proto__ in production code?

`__proto__` is standardized in ES2015 Annex B for web compatibility and works in all major environments. However, `Object.getPrototypeOf()` and `Object.setPrototypeOf()` are the standard API. For reading `[[Prototype]]`, prefer `Object.getPrototypeOf(obj)`. Using `__proto__` in direct object literals (`{ __proto__: proto }`) is the recommended way to set prototype in a literal — that specific usage is standardized.

Does every function have a prototype property?

rrow functions are the exception — they do NOT have a `prototype` property and cannot be used as constructors. `const fn = () => {}; fn.prototype; // undefined; new fn(); // TypeError`.

What happens if __proto__ and prototype point to different objects?

That should not happen for instances created with `new Constructor` — by definition `instance.__proto__ === Constructor.prototype`. However, if you manually reassign `Constructor.prototype` after creating instances, existing instances' `__proto__` still points to the OLD prototype. New instances get the new prototype. This is a common source of subtle inheritance bugs.

Is there a performance penalty for long prototype chains?

Yes, but it is small. Each property lookup that fails at a level causes one hop up the chain. A chain of 3-4 levels (typical for class inheritance) is negligible. JavaScript engines cache these lookups with inline caches. Deep, artificially long chains (10+ levels) can measurably slow property access in hot loops.

How is proto different from [[Prototype]]?

`[[Prototype]]` is the specification's name for the internal slot that holds the prototype link — it is notation, not real JavaScript syntax. `__proto__` (and `Object.getPrototypeOf`) is the way to read/write `[[Prototype]]` from JavaScript code. In practice they refer to the same thing.

Conclusion

prototype is a property on constructor functions — it provides the shared methods that new-created instances will inherit. __proto__ is the live [[Prototype]] link on each instance — it points to that shared prototype object. The new operator connects them: the instance's __proto__ is set to the constructor's prototype. Understanding this connection is the key to understanding the prototype chain, instanceof, method sharing, and how class extends works at the runtime level.