Objects can have a prototype and a prototype is just another object. We can get
an object’s prototype using the Object.getPrototypeOf
method.
Object.getPrototypeOf({}); // → {}
Yes, JavaScript really loves objects. It’s objects all the way down. Actually that’s not completely true:
Object.getPrototypeOf(Object.getPrototypeOf({})); // → null
The first call to Object.getPrototypeOf
gave us the prototypical object—the
one that acts as the prototype for practically all objects—and calling
Object.getPrototypeOf
on that gives us null
. You’ve got to stop somewhere,
right?
Things like arrays and strings are objects too so they also have prototypes:
Object.getPrototypeOf(""); // → [String: '']
Object.getPrototypeOf([]); // → []
As you can see they have a different prototype to vanilla objects, but this is only in the immediate sense:
Object.getPrototypeOf(Object.getPrototypeOf("")); // → {}
Object.getPrototypeOf(Object.getPrototypeOf([])); // → {}
Prototypes can be seen as ancestors: an object being the descendent of it’s prototype. In the previous case an array is a descendent of the array prototype which is a descendent of the object prototype etc.
Why?
An object shares the properties of it’s prototype so it’s a convenient way of sharing data and code between a set of similair objects.
let proto = {
x: 1
};
let foo = {
y: 2
};
Object.setPrototypeOf(foo, proto);
console.log(foo.x); // → 1
Constructors
The canonical way of constructing an object based on a prototype is by defining
a function. These types of functions are called constructors. There’s nothing
particularly special about the functions themselves—but rather how they’re
used. Rather than calling them like a normal function, we use the new
keyword. This binds uses of the this
keyword in the body of the function to a
newly created object.
function Constructor() {
console.log(Object.keys(this).length);
}
// `this` is bound to the global object
Constructor(); // → 22
// `this` is bound to a newly created object
new Constructor(); // → 0
All user-defined functions have a prototype
property.
function foo() {}
console.log(foo.prototype); // → foo {}
This property contains an object unique to this function.
All new objects created using this function (and the new
keyword), have this
unique object set as their prototype.
function Constructor() {}
let foo = new Constructor();
console.log(Constructor.prototype === Object.getPrototypeOf(foo)); // → true
Any changes to this prototype object will be reflected in all objects created using it.
function Constructor() {}
let foo = new Constructor();
let bar = new Constructor();
Constructor.prototype.moo = () => console.log("Moo.");
foo.moo(); // → Moo.
bar.moo(); // → Moo.
I hope this article has left you less confused than when you started reading it.
Love and kisses,
Simon