JavaScript Prototypes

Every JavaScript object has an internal link to another object called its prototype. This prototype chain is the mechanism by which JavaScript implements inheritance. When you access a property on an object, JavaScript looks up the prototype chain until it finds the property or reaches null.

The Prototype Chain

Objects inherit properties and methods from their prototype. If a property is not found on the object itself, JavaScript looks at the object's prototype, then the prototype's prototype, and so on until it reaches null.

Prototype Chain Lookup
const animal = {
  type: 'Animal',
  describe: function() {
    return 'I am a ' + this.type;
  }
};

const dog = Object.create(animal);
dog.type = 'Dog';
dog.bark = function() { return 'Woof!'; };

// dog -> animal -> Object.prototype -> null
console.log(dog.describe());
console.log(dog.bark());
console.log(dog.toString());
console.log(dog.type);

__proto__ vs prototype

The __proto__ property is the actual link to the prototype object on every instance. The prototype property exists only on functions/constructors and is the object that will become the __proto__ of instances created with new.

__proto__ vs .prototype
function Person(name) {
  this.name = name;
}

Person.prototype.greet = function() {
  return 'Hi, I am ' + this.name;
};

const alice = new Person('Alice');

// .prototype is on the constructor function
console.log(typeof Person.prototype);

// __proto__ is on the instance (links to Person.prototype)
console.log(alice.__proto__ === Person.prototype);

// The method is on the prototype
console.log(alice.greet());
console.log(alice.hasOwnProperty('name'));
console.log(alice.hasOwnProperty('greet'));

Object.getPrototypeOf()

Object.getPrototypeOf() is the recommended way to get an object's prototype. It is preferred over the deprecated __proto__ property.

Getting the Prototype
const arr = [1, 2, 3];
const obj = { a: 1 };
const str = new String('hello');

// Get prototype (recommended way)
console.log(Object.getPrototypeOf(arr) === Array.prototype);
console.log(Object.getPrototypeOf(obj) === Object.prototype);
console.log(Object.getPrototypeOf(str) === String.prototype);

// Prototype chain of an array:
// arr -> Array.prototype -> Object.prototype -> null
const arrProto = Object.getPrototypeOf(arr);
const objProto = Object.getPrototypeOf(arrProto);
const nullProto = Object.getPrototypeOf(objProto);
console.log('Array proto is Array.prototype: ' + (arrProto === Array.prototype));
console.log('Next is Object.prototype: ' + (objProto === Object.prototype));
console.log('End of chain: ' + nullProto);

Object.create()

Object.create() creates a new object with the specified prototype. This is a clean way to set up inheritance without using constructor functions.

Creating Objects with Specific Prototypes
const vehicle = {
  type: 'vehicle',
  start: function() {
    return this.type + ' started';
  },
  stop: function() {
    return this.type + ' stopped';
  }
};

// Create car with vehicle as prototype
const car = Object.create(vehicle);
car.type = 'car';
car.drive = function() {
  return 'Driving ' + this.type;
};

console.log(car.start());
console.log(car.drive());
console.log(car.stop());

// Object with no prototype
const bare = Object.create(null);
console.log('No prototype: ' + Object.getPrototypeOf(bare));

Inheritance via Prototypes and hasOwnProperty()

The hasOwnProperty() method checks if a property belongs directly to an object (not inherited from the prototype chain). This is important when iterating over object properties.

hasOwnProperty() and Inheritance
function Shape(color) {
  this.color = color;
}
Shape.prototype.getColor = function() {
  return this.color;
};

const circle = new Shape('red');
circle.radius = 5;

// Own properties vs inherited
console.log('color own: ' + circle.hasOwnProperty('color'));
console.log('radius own: ' + circle.hasOwnProperty('radius'));
console.log('getColor own: ' + circle.hasOwnProperty('getColor'));

// for...in includes inherited properties
for (const key in circle) {
  const own = circle.hasOwnProperty(key) ? '(own)' : '(inherited)';
  console.log(key + ' ' + own);
}
ConceptDescription
__proto__Link to the prototype on every object (deprecated)
prototypeProperty on functions; becomes __proto__ of instances
Object.getPrototypeOf()Standard way to get an object's prototype
Object.create(proto)Create object with a specific prototype
hasOwnProperty()Check if property is directly on the object
Prototype chainLookup path: object -> proto -> proto -> null
📝 Note: Modifying built-in prototypes (like Array.prototype or Object.prototype) is called monkey patching and is strongly discouraged. It can break other code and libraries. ES6 classes provide a cleaner syntax for prototype-based inheritance.
Exercise:
What does Object.create(proto) do?
Try it YourselfCtrl+Enter to run
Click Run to see the output here.