Prototypes in JavaScript

Prototypes in JavaScript are the mechanism that lets objects inherit properties and methods from other objects. This topic sits at the heart of the language because JavaScript does not use class based inheritance in its original object model. Instead, objects are linked to other objects through a prototype relationship, and property lookup can travel through that chain when the property is not found directly on the object itself.

Many JavaScript features become easier to understand once prototypes are clear. Objects, constructor functions, arrays, built in methods, classes, and inheritance syntax all depend on prototype behavior underneath. Developers can use JavaScript productively without mastering every internal detail immediately, but once applications become larger, prototype knowledge stops being optional and starts becoming practical.

Why prototypes matter

JavaScript objects are dynamic containers of data, but they are also connected. When you access a property on an object, JavaScript first checks whether that property exists directly on the object. If it does not, the engine looks at the object’s prototype. If the property is not there either, JavaScript continues upward through the prototype chain. This lookup model is how shared behavior is reused across related objects without copying every method into every instance.

This matters for memory usage, code organization, and debugging. Shared methods can live in one place instead of being recreated for every object. At the same time, understanding where a property actually comes from helps explain why some values seem to appear on objects even when they were never assigned directly. The answer is often that the value lives somewhere higher in the prototype chain.

The basic idea of a prototype chain

Every normal JavaScript object has an internal prototype link. That link points to another object or to null. When property lookup fails on the current object, JavaScript follows that link and checks the next object. If the property is still not found, the search continues until the chain ends. This chain is why methods like `toString`, `hasOwnProperty`, or array methods are available even though they are not assigned directly on every value you create.

const user = {
  name: "Riya"
};

console.log(user.name);
console.log(user.toString()); javascript

In this example, `name` is an own property of `user`, but `toString` is inherited. JavaScript does not find `toString` directly on `user`, so it moves upward through the prototype chain until it reaches an object that provides that method. This is one of the clearest demonstrations of prototype lookup in everyday code.

Own properties versus inherited properties

An own property is stored directly on the object. An inherited property comes from the prototype chain. This distinction matters when checking whether a value truly belongs to the object or is merely available through inheritance. It affects loops, serialization decisions, debugging, and any code that wants to treat explicitly stored values differently from shared behavior.

ConceptMeaningTypical Use
Own propertyStored directly on the objectObject specific data such as name or age
Inherited propertyFound through the prototype chainShared methods such as array helpers
Prototype chainLinked lookup path between objectsBehavior reuse without duplication

When developers confuse own and inherited properties, bugs become harder to explain. An object may appear to have a method because it can call it successfully, but that does not mean the method is defined locally. Knowing the difference prevents incorrect assumptions about where behavior is coming from.

Constructor functions and prototype sharing

Before modern class syntax became popular, constructor functions were a common way to create related objects. A constructor function can assign object specific data inside the function body, while shared methods are placed on the function’s `prototype` object. Every instance created with `new` can then inherit those methods through the prototype chain.

function Person(name) {
  this.name = name;
}

Person.prototype.sayHello = function () {
  return "Hello, " + this.name;
};

const a = new Person("Asha");
const b = new Person("Karan");

console.log(a.sayHello());
console.log(b.sayHello()); javascript

This pattern is important because `sayHello` is created once and shared by all instances. If the method were declared inside the constructor body, each new object would receive its own separate function copy. The prototype based approach is usually cleaner when the behavior should be common to every instance.

What the prototype property on functions means

One common source of confusion is the word `prototype` itself. Constructor functions have a `prototype` property, but ordinary objects have an internal prototype link. These are related but not identical concepts. The constructor’s `prototype` object becomes the prototype of instances created with `new`. In other words, the function provides the shared template object, and the instances are linked to it.

This is why `Person.prototype.sayHello` becomes available on every object created by `new Person(…)`. The instances do not copy the method. They inherit access to it because their internal prototype points to `Person.prototype`.

Creating objects with Object.create

JavaScript also allows developers to create an object with an explicitly chosen prototype using `Object.create`. This is a direct way to express prototype based inheritance without involving a constructor function. It is often useful when you want one object to act as the base behavior provider for another.

const animal = {
  speak() {
    return "generic sound";
  }
};

const dog = Object.create(animal);
dog.name = "Bolt";
dog.speak = function () {
  return this.name + " barks";
};

console.log(dog.speak()); javascript

Here, `dog` inherits from `animal`, but it also overrides `speak` with its own version. This shows two important prototype ideas at once: inherited lookup and local override. If an own property exists, JavaScript uses it first. Only when the local property is missing does the engine move upward through the chain.

Prototype lookup and shadowing

When an object defines a property with the same name as an inherited property, the local value shadows the inherited one. JavaScript stops the lookup at the first successful match. This rule is simple but powerful because it explains how objects can reuse shared behavior while still customizing selected pieces. It also explains why changing a prototype method can affect many objects at once unless some of them already define a method with the same name locally.

Shadowing is not the same as editing the prototype. If an object stores its own property, that local property only affects that one object. If the prototype object is changed, every linked object that relies on inherited lookup can observe the change. This is why careless prototype mutation in large codebases can create wide ripple effects.

Built in prototypes

Arrays, functions, dates, maps, and many other built in structures use prototypes. Methods such as `push`, `map`, `filter`, and `slice` come from `Array.prototype`. Function helpers come from `Function.prototype`. Common object utilities come from `Object.prototype`. Understanding this makes the language feel less magical. These methods are not floating in space. They live on prototype objects that instances inherit from.

This also explains why extending built in prototypes is considered risky in most production code. If a developer adds a custom method to a built in prototype, that method can appear everywhere and may collide with other code, libraries, or future language features. The language makes it possible, but engineering discipline usually says to avoid it.

Classes still use prototypes underneath

Modern `class` syntax makes object oriented JavaScript easier to read, but it does not replace prototypes. Class methods are still stored on the prototype of the class, and instances still use prototype lookup. The class syntax is mostly a cleaner way to express patterns that already existed in the language. This is why prototype knowledge remains relevant even in codebases that mostly use classes instead of constructor functions.

Common mistakes with prototypes

  • Assuming a property belongs directly to an object just because the object can access it.
  • Confusing a constructor function’s `prototype` property with an instance’s internal prototype link.
  • Placing shared methods inside constructors and recreating them for every instance.
  • Mutating built in prototypes in application code and creating global side effects.
  • Forgetting that local properties shadow inherited ones during lookup.

Best practices

Use prototypes deliberately when behavior should be shared. Prefer clear designs where object specific data lives on the instance and common methods live on the prototype. Use classes when they improve readability, but remember they are still built on the same mechanism. When debugging, ask whether a property is own, inherited, or shadowed. That question often resolves confusion quickly.

Prototypes are one of the reasons JavaScript feels different from strictly class based languages. Once the prototype chain becomes intuitive, many other topics such as classes, inheritance, built in methods, and object reuse start fitting together much more naturally.

FAQ

What is a prototype in JavaScript?

A prototype is an object that another object can inherit properties and methods from through the prototype chain.

Do JavaScript classes replace prototypes?

No. Classes are syntax on top of the same prototype based object model.

Why are shared methods placed on the prototype?

Because that lets many instances reuse one method definition instead of creating a separate copy for every object.

Reading prototype behavior in real code

In practical debugging, prototype awareness often answers questions that otherwise feel strange. If two objects can both call the same method, it does not necessarily mean both objects store that method directly. It may mean they inherit it from the same prototype object. Checking where a property actually comes from is often the difference between random guessing and understanding the language model clearly.

This also matters when performance and memory discussions appear. Shared prototype methods usually make more sense than recreating identical functions on every instance. That is one reason prototype based reuse remains important even when developers mostly write modern class syntax.


Continue learning JavaScript in order
Follow the topic sequence with the previous and next lesson.