Prototypes in JavaScript
TL;DR: [[Prototype]]
is an internal property that exists on all JS objects. It can be accessed via the __proto__
property. .prototype
is an object which exists only on function constructors and is used to set a constructed object's internal [[Prototype]]
If you have ever been confused about all the different kinds of prototypes in JavaScript, you are not alone. This article is an overview and explanation of the different kinds of prototypes and their purposes in JavaScript.
[[Prototype]]
In the ECMAScript specifications double brackets denote internal properties and are, according to the specs, not part of the ECMAScript language. This means that those internal properties are not accessible by the objects that contain them.
[[Prototype]]
is an internal property on all JS objects that points to the object's prototype. Every JS object has an internal [[Prototype]]
. This goes for for objects created with object literals, those created with the new
keyword and also for those created with Object.create()
. Further, even function constructors have an internal [[Prototype]]
.
__proto__
Since [[Prototype]]
is an internal property it was never meant to be accessible by developers, according to the ECMA specs. At some point, however, browser vendors and other environments started giving access to the internal [[Prototype]]
object via the __proto__
property.
This means that __proto__
and [[Prototype]]
refer to the same thing.__proto__
is simply a way to access the internal [[Prototype]]
in some JS environments.
The internal [[Prototype]]
is a regular JS object and thus also has an internal [[Prototype]]
and so on. This goes all the way down to Object.prototype
. The internal[[Prototype]]
of Object.prototype
is null. This is called the prototype chain. More on that later.
.prototype
The .prototype
property only exists on function constructors! When you define a function, the constructor is given an almost empty .prototype
object for free. The only property on this object is .constructor
, which points back to the function constructor, i.e. .prototype
has a property .constructor
and vice versa.
When you create a new object with the new
keyword, its internal[[Prototype]]
is set to the .prototype
of the function constructor that was used to create the object.
Object creation prototype chain
Let's look at how the internal [[Prototype]]
object is set when creating an object with the new
keyword, as an object literal and with Object.create()
.
new keyword
When you create a new object with the new
keyword, that object's internal [[Prototype]]
is set to the constructor function's .prototype
function Car(){}
const mCar = new Car();
mCar.__proto__ === Car.prototype; // true
Object literal
When you create a new object with the object literal syntax the object's internal [[Prototype]]
is set to Object.prototype
.
const obj = {};
obj.__proto__ === Object.prototype; // true
Object Create
When you create a new object with Object.create()
, the internal [[Protoype]] of the created object is set to the object passed in as the first argument to Object.create()
.
const car = {
wheels: 4
};
const mCar = Object.create(car);
mCar.__proto__ === car // true
Prototype chain
Since the internal [[Prototype]]
object is simply that, an object, it too has an internal [[Prototype]]
, which in turn, yada yada yada.
This leads to a prototype chain.
If a prototype is not specified for an object explicitly, then the default value for __proto__
is taken: Object.prototype
.Object.prototype
itself also has a __proto__
, which is the final link of the chain and is set null
.
If a property or method isn't found on an object instance, the prototype chain is traversed. The first found property/method with the same name is used. If none is found in the entire chain, all the way down to Object.prototype, undefined is returned.
Properties in the prototype chain are called 'inherited properties' as opposed to properties on the instances themselves which are 'own properties'.