So, what are classes? … really?
Classes as machines
So, a class is a machine — in goes some parameters available only at runtime and out comes an object with the properties and behaviour as dictated by the recipe. That thinking can be directly modelled as a function that takes some parameters and produces an object with the right properties and behaviour.
In what follows, I’ll omit the parameters for simplicity of presentation and you can always add them back later. So we have -
This gives a “first order” view of classes. Though a useful way of thinking
in itself, you soon hit a modelling wall the moment you want another class
that is only slightly different from one you already wrote. You cringe at
having to copy-paste code to produce make objects of this slightly different
variety. What you want is some way to code up only the incremental difference
One way out is to write a function that can combine the properties and behaviour
of two given objects – call it
blend. So that will let you write -
NewBehaviour is a function that models only what is different
The above approach, though valid, is limited by the inability of
to have any kind of say over the changes to be introduced to the original object.
It would be ideal if
NewBehaviour could first take a look at the object produced
MyClass before it makes any changes to it. Therefore,
NewBehaviour is better
written as a function that takes in the object produced by
MyClass and returns
a new object.
NewBehaviour can now be made arbitarily flexible. In retrospect, we could’ve
MyClass with an extra object parameter, so that the combination
of these two classes itself looks like an “object transformation machine”.
1 2 3 4 5
Multiple inheritance and “mixins”
The traditional notion of multiple inheritance froma number of classes
so on in a specific order is then merely repeated application of
Inherit. If we have
an array of classes
ClassArr and we want to make a class that “inherits” from all of them,
all we need to do is –
.. which we can abstract again as a function that takes an array of classes and produces a “multiply inherited class”.
1 2 3
Modify or copy?
Notice that we haven’t placed any constraints on these functions so far with respect to whether they destructively modify the object passed in, or return another object with the necessary properties. Both these are acceptable and you may want to weigh which approach is suitable for you. For instance, creating copies costs memory, but you trade off a one time performance hit for repeated searches up an “inheritance tree”.
Let us take a simple example of inheritance to make further digging easier to
follow. Say we have an
Image class that can produce objects that know how
draw themselves into a
context in portrait mode.
1 2 3 4 5 6 7 8 9 10
That was easy enough, but say we now want a variant of
Image which produces
objects that draw themselves in landscape mode. i.e., we want -
How would we write the
1 2 3 4 5 6 7 8 9 10 11
Simple enough? … Oops! We’ve just introduced an infinte draw loop because the landscape object’s draw keeps calling itself!
To solve this, we need to first store away the object’s original
draw function and use that one within
1 2 3 4 5 6 7 8 9 10 11 12 13
This is then the approach to use to model calling the “parent method” within a “child class”.
The “prototype chain”
access to another object if they aren’t part of itself. Though we can
set up arbitrary numbers of such delegation chains ourselves, the builtin
mechanism serves to ease single inheritance cases. Our
can now be written as -
1 2 3 4 5 6 7 8 9 10 11 12 13
Object.create makes a new object that looks and behaves exactly like
but now when you add a new property to
extendedObj, the original
remains unmofied. Now, we don’t need to save away old method definitions
because the old object will always have them and so we can refer to the old
draw function as just
So, what’s up with the weird
obj.draw.call(extendedObj, context) and why
can’t we just write
To answer that, we need to look at what
It means “draw
obj making use of its properties”. If the
needs to know the
width of the object to do the drawing, it will access
it will get
obj.width within the draw function. If we later on modify the
width of the
extendedObj, that modified value will not be accessible when
obj does not know anything about the existence
The solution then, is to tell the
obj.draw function to run, but ask it to
make use of the properties of
extendedObj instead. Now, since
otherwise has all the properties of
obj, there is no information loss to the
obj.draw function. The way we tell the
obj.draw function to do that is to
call method of the
The above code, btw, is equivalent to the following -
Let’s take a second look at the implementation of
Image and the
function in particular –
1 2 3 4 5 6 7 8
If the draw function was originally implemented like above, then it would
be of no use to try to change the context using
Function.call like in
old.draw.call(extendedObj) because the body of
old.draw always explicitly
refers to the properties of
Oops! It looks like the original
Imageimplementation cannot be extended, although it would work fine on its own!
What we want in this case, is for the
draw function to be more generic and
pull properties from an arbitrary object. That is done using the dynamically
this argument, which, when used within the body of a function, refers
to the object that the caller passed as the first argument to the
Here is a modified
1 2 3 4 5
When this draw function is called with
extendedObj will substitute
this within the funciton body,
and we get the correct behaviour. When you simply call
draw name figures out that
must be the
this that the caller intended.
Therefore, in a prototype based object system, methods need to be explicitly designed to support extension through prototype chains.
obj.__proto__ property lurking in the
shadows, which refers to the object that will be looked up should some property
obj not be found in it. This is called the object’s “prototype”
using this prototype chain - the
new does is exactly what the function
make shown below does -
1 2 3 4 5
Since the newly created
obj is passed as the context in
the body of
ClassFn can refer to the object whose properties need to
be filled in simply using
this and it need not bother creating a
new object as well, since one is already available.
Note: There is nothing special about
ClassFn.prototype and if you’re
writing your own
make function, you can store a prototype object in
any field of
ClassFn. It just happens to be the property that
refers to in the bulitin implementation.
Wrapping it all up
Model classes as functions that produce objects.
Model inheritance and “mixins” using higher order functions and function composition.
When implementing methods, be aware of whether you want the method to be extensible via the prototype chain or not. If a method with a bound object in its body is called, it might end up modifying the behaviour of all the objects that it is a prototype of!
Always be aware of what
thisvalue is being implicated in any function or method call, by translating