Introduction
Prototypes are the mechanism by which JavaScript objects inherit properties from each other. In this article, we explain what a prototype is, how prototype chains work, and how to set a prototype for an object.
Prerequisites
Understand JavaScript functions, familiarize yourself with JavaScript fundamentals (see Getting Started and Building Blocks), and OOJS principles (see Introduction to Objects).
Prototype chain
In the browser console, try creating a literal object:
const myObject = {
city: "Madrid",
greet() {
console.log(`Greetings from ${this.city}`);
},
};
myObject.greet(); // Greetings from MadridThis is an object with one data property, city, and one method, greet(). If you type the object name followed by a period into the console, like myObject., the console will display a list of all the properties available for this object. You'll see that there are many more properties in addition to city and greeting!
__defineGetter__
__defineSetter__
__lookupGetter__
__lookupSetter__
__proto__
city
constructor
greet
hasOwnProperty
isPrototypeOf
propertyIsEnumerable
toLocaleString
toString
valueOfTry to access one of them:
myObject.toString(); // "[object Object]"
It works (even if it's not clear what toString() does).
What are these additional properties and where do they come from?
Every object in JavaScript has a built-in property called a prototype. A prototype is an object in itself, so a prototype will have its own prototype, creating what is called a prototype chain. This chain ends when we reach a prototype that has null for its prototype.
Note: A property of an object that points to its prototype is not called a prototype. The name is not standard, but in practice all browsers use __proto__. The standard way to access an object's prototype is the Object.getPrototypeOf() method.
When you try to access a property of an object: If the property is not found in the object itself, the prototype for the property is searched. If the property is still not found, the instance prototype is searched, and so on until the property is found or the end of the chain is reached, in which case undefined is returned.
So when we call myObject.toString(), the browser:
- Looks for toString in myObject
- I can't find it there, so it looks in the myObject object prototype for toString
- He finds it there and calls it.
What is the prototype for myObject? To find out, we can use the Object.getPrototypeOf() function:
Object.getPrototypeOf(myObject); // Object { }
This is an object called Object.prototype and is the most basic prototype that all objects have by default. The Object.prototype prototype is null, so it is at the end of the prototype chain:
The prototype of an object is not always Object.prototype. Try this:
const myDate = new Date();
let object = myDate;
do {
object = Object.getPrototypeOf(object);
console.log(object);
} while (object);
// Date.prototype
// Object { }
// nullThis code creates a Date object, then moves up the prototype chain and registers the prototypes. It shows us that the prototype of myDate is a Date.prototype object, and its prototype is Object.prototype .
In fact, when you call familiar methods, like myDate2.getTime(), you are calling a method that is defined in Date.prototype.
Shading properties
What happens if you define a property on an object when a property with the same name is defined in the object's prototype? Let me see:
const myDate = new Date(1995, 11, 17);
console.log(myDate.getTime()); // 819129600000
myDate.getTime = function () {
console.log("something else!");
};
myDate.getTime(); // 'something else!'This should be predictable given the description of the prototype chain. When we call getTime(), the browser first looks in myDate for a property with that name and only checks the prototype if myDate does not define it. So when we add getTime() to myDate, the version in myDate is called.
This is called “shadowing” the property.
Setting up a prototype
There are several ways to set the prototype of an object in JavaScript, and here we will describe two: Object.create() and the constructor.
Using Object.create
The Object.create() method creates a new object and allows you to specify an object to be used as the prototype of the new object.
Here is an example:
const personPrototype = {
greet() {
console.log("hello!");
},
};
const carl = Object.create(personPrototype);
carl.greet(); // hello!Here we create a personPrototype object that has a greet() method. We then use Object.create() to create a new object with personPrototype as its prototype. Now we can call greet() on the new object and the prototype will provide its implementation.
Using the constructor
In JavaScript, all functions have a property called prototype. When you call a function as a constructor, this property is set to the prototype of the newly created object (by convention, in a property named __proto__).
So if we set the prototype of a constructor, we can ensure that all objects created with that constructor are given that prototype:
const personPrototype = {
greet() {
console.log(`hello, my name is ${this.name}!`);
},
};
function Person(name) {
this.name = name;
}
Object.assign(Person.prototype, personPrototype);
// or
// Person.prototype.greet = personPrototype.greet;Here we create:
- A personPrototype object that has a greet() method
- A Person() constructor function that initializes the name of the person to be created.
Then we put the methods defined in personPrototype into the prototype property of the Person function using Object.assign.
After this code, objects created using Person.prototype() will receive as their prototype, which automatically contains the greet method.
const reuben = new Person("Reuben");
reuben.greet(); // hello, my name is Reuben!This also explains why we said earlier that the prototype of myDate is called Date.prototype: this is the prototype property of the Date constructor.
Own property
The objects we create using the Person constructor above have two properties:
- A name property, set in the constructor, so it appears directly on Person objects
- A greet() method set in the prototype.
It's common to see this pattern, where methods are defined on the prototype, but data properties are defined in the constructor. The reason is that the methods are usually the same for every object we create, while we often want each object to have its own unique value for its data properties (as here each person has a different name).
Properties that are defined directly on the object, like name here, are called own properties, and you can check whether a property is a specific property using the static Object.hasOwn() method:
const irma = new Person("Irma");
console.log(Object.hasOwn(irma, "name")); // true
console.log(Object.hasOwn(irma, "greet")); // falseNote: You can also use the non-static Object.hasOwnProperty() method here, but we recommend using Object.hasOwn() if possible.
Prototypes and Inheritance
Prototypes are a powerful and highly flexible JavaScript feature that allows for code reuse and object composition.
They specifically support a version of inheritance. Inheritance is a feature of object-oriented programming languages that allows programmers to express the idea that some objects in a system are more specialized versions of other objects.
For example, if we are modeling a school, we might have teachers and students: they are both people, so they share some properties (e.g., they both have names), but each may add additional properties (e.g., teachers have a subject they teach), or they may implement the same property in different ways. In an OOP system, we might say that teachers and students both inherit from People.
You can see how in JavaScript, if Professor and Student objects can have Person prototypes, they can inherit common properties, while adding and redefining properties that need to be different.
In the next article, we will discuss inheritance along with other core features of object-oriented programming languages and see how JavaScript supports them.
Result
This article covers JavaScript object prototypes, including how object prototype chains allow objects to inherit properties from each other, the prototype property and how to use it to add methods to constructors, and other related topics.









