All objects in JavaScript have an internal property called the prototype. Prototypes are used in JavaScript to reuse code and combine objects. This section will help you understand how prototype object chains allow objects to inherit features from one another. It will also explain how the prototype property is used to add methods to constructors.
The prototype chain
const myObject = { city: "Madrid", greet() { console.log(`Greetings from ${this.city}`); }, }; myObject.greet(); // Greetings from Madrid
This is an object with one data property, city
, and one method, greet()
. If you type the object's name followed by a period into the console, like myObject.
, then the console will pop up a list of all the properties available to this object. You'll see that as well as city
and greet
, there are lots of other properties!
__defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ __proto__ city constructor greet hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf
Try accessing one of them:
myObject.toString(); // "[object Object]"
It works (even if it's not obvious what toString()
does).
What are these extra properties, and where do they come from?
Every object in JavaScript has a built-in property, which is called its prototype. The prototype is itself an object, so the prototype will have its own prototype, making what's called a prototype chain. The chain ends when we reach a prototype that has null
for its own prototype.
Note: The property of an object that points to its prototype is not called prototype
. Its 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 can't
be found in the object itself, the prototype is searched for the
property. If the property still can't be found, then the prototype's
prototype is searched, and so on until either 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
inmyObject
- can't find it there, so looks in the prototype object of
myObject
fortoString
- finds it there, and calls it.
What is the prototype for myObject
? To find out, we can use the function Object.getPrototypeOf()
:
Object.getPrototypeOf(myObject); // Object { }
This is an object called Object.prototype
, and it is the most basic prototype, that all objects have by default. The prototype of Object.prototype
is null
, so it's 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 { } // null
This code creates a Date
object, then walks up the prototype chain, logging the prototypes. It shows us that the prototype of myDate
is a Date.prototype
object, and the prototype of that is Object.prototype
.
In fact, when you call familiar methods, like myDate2.getMonth()
,
you are calling a method that's defined on Date.prototype
.