In Javascript, objects store key/value pairs. The keys can be strings or symbols and values can be anything. A key with a function for a value is called a method. A value can also be another object. Key/value pairs in objects are called properties.
An object can be used as the template for another object. If an object is the template for another object, it’s called a prototype.
Functions can build objects and return them. A function that builds objects is usually called either a constructor or an object factory. They’ll be described later.
Every object has a built-in property called __proto__
.* The value of __proto__
is the object’s prototype, the object which was its template.
* __proto__
is actually a property on Object.prototype
. It has getter & setter methods for a property called [[Prototype]]
that every object has. [[Prototype]]
is what forms the link between objects.
If the object was created without specifying a prototype, the prototype will be Object.prototype
; this is ultimately the ancestor of most objects, and it’s where objects can inherit built-in methods from. Object.prototype
has no prototype – its __proto__
is null
.
An object can be made from another object using Object.create
, like this:
var a = {
firstThing: 'one',
myMethod: function() {
console.log('an object’),
};
var b = Object.create(a);
console.log(b.firstThing()); // ‘one’
b.myMethod(); // ‘an object’
The object b
has access to all the properties in a
. The object b
‘s __proto__
is set equal to object a
, making a
the prototype of b
. You can check whether an object is the prototype of another using
a.isPrototypeOf(b); //returns true here
or check what an object’s prototype is using
Object.getPrototypeOf(b); //returns a
You can also compare the __proto__
properties directly to check inheritance:
console.log(b.__proto__ === a); // true
If you try to access an object’s property, Javascript will first check for the method in the object itself. If it’s not there, it checks the object’s prototype. If not found there, it looks in that object’s prototype, and continues until it reaches Object.prototype
, the ancestor of most objects. If it’s not there, it returns an error. If the property is found, the first matching property will be returned; if an object b
has a property with the same name as its prototype a
, the one in b
will be returned:
var a = { prop: ‘I\’m in a’, };
var b = Object.create(a);
var c = Object.create(b);
b.prop = ‘I\’m in b’;
console.log(c.prop); // ‘I’m in b’
This is called a prototype chain. It lets you make an object b
from a prototype object a
, and b
will have access to the properties of a
. Having an object access the properties of another object through a prototype chain is called prototypal inheritance or behavior delegation.
Creating objects from other objects using Object.create
is called the objects-linking-to-other-objects pattern, or OLOO for short.
Functions can also make objects. One type of function which makes objects is called an object factory. When a function is used as an object factory, the object it returns has its own copy of the function’s properties. You also can’t tell whether it was made by a factory function. In the following example, the object obj
has its own copy of functions a
and b
; they don’t reference makeObj
:
function makeObj() {
var a = function() { console.log('A'); };
var b = function() { console.log('B'); };
return {
logA: a,
logB: b,
};
}
var obj = makeObj();
obj.logA(); // 1
obj.logB(); // 2
If the function is used as a constructor, however, the object will know who made it. It will also be able to access properties defined by the function instead of getting its own copy.
You can make an object using a constructor like this:
Function C() { // Constructor names are usually capitalized.
this.value = 5;
this.getValue = function() {
return this.value;
};
}
var obj = new C();
Using the new
keyword is what tells Javascript to execute the function C
as a constructor. Otherwise, it would execute as a normal function, and this
would refer to the global object, which is probably not what you want.
The object obj
will inherit the properties defined in C
. When you use the new
command with a function, Javascript does the following:
- Makes a new, anonymous object
- Sets the function’s
prototype
property to the new object - Sets
this
to point to the new object - Sets the new object’s
constructor
property equal to the function - Executes the code in the function
- Returns the new object unless a different object is explicitly returned*
* You have to return an object to override this. If you try to return a primitive type, Javascript ignores it and returns the new object anyway.
In the returned object, the __proto__
is set equal to the function’s prototype
object. This lets the new object have access to the properties of the constructor function’s prototype
.
Properties can be added to an object after it’s created. If you add properties to an object which is being used as a prototype, other objects created from the prototype can access the new properties immediately.
Every function has a prototype
property. The prototype
holds methods that the constructor makes available to objects that inherit from it.
You can add new properties to a constructor function also. Instead of modifying the function itself, properties are usually added to the function’s prototype
object, like this:
aFunction.prototype.newMethod = function() {
console.log(‘I\’m a new method’);
};
Since a function’s prototype
is a normal object, other objects which inherit from it can access new properties immediately also. The process of creating objects with constructor functions and the new
keyword, and adding methods to the prototype
, is called the pseudo-classical pattern.
This is how inheritance in Javascript differs from other object oriented languages. In most other languages, a class is like a pattern or mold that objects are instantiated from. To add functionality to the class, you have to modify the class and create new objects. But in Javascript, inheritance is done using objects linked together. A prototype can be modified on the fly, and objects already inheriting from it can use the new properties.
Since functions are objects too, they also have a __proto__
property. It normally points to Function.prototype
, which has the built-in methods that most functions get to use.
A constructor function can inherit properties from another constructor. The simplest way to do this would be to set their prototypes equal, like this:
Fir.prototype = Tree.prototype;
objects created from Fir
will inherit the properties from Tree
– but the reverse is also true. Since the prototypes are now the same object, properties added to it are available to objects created from either constructor. This might or might not be what you want.
Another way to have a constructor inherit from another is to set the prototype
of one equal to an object created from the prototype
of another, like this:
Fir.prototype = Object.create(Tree.prototype);
This way, Fir
’s prototype still gets the properties from Tree
, but it’s set to an object made from Tree.prototype
instead of pointing to Tree.prototype
itself. Fir
now inherits from Tree
, but not the reverse.
You’ll also need to set Fir.prototype
‘s constructor to point back to its new function, like this:
Fir.prototype.constructor = Fir;
Otherwise, Fir.prototype.constructor
would still point to Tree
.
This way is analogous to how other languages do class inheritance.
In conclusion, Javascript implements inheritance through linking objects together, called a prototype chain. Being able to look up properties somewhere else on the prototype chain is called prototypal inheritance or behavior delegation. Since there are different ways to create and link objects, one of several patterns is usually used. Some examples here were object factories, pseudo-classical and objects-linking-to-other-objects (OLOO) patterns.
If at first you don’t succeed, you’ll have to write your own blog post.