constructor function and append each method to its
property. To create an instance of the object, one must then use the
new keyword on a call to the constructor function.
Set is the constructor function; in its body,
this is a
reference to a newly-created object that will be returned after a call
to the constructor with the
new keyword. By convention, constructor
functions begin with a capital.
Set is just a regular function, i.e. we can call it
new keyword. Though usually it is not what you want,
Set will return
undefined and will bind
this to the global
object in the absence of a caller.
new keyword applied to the
Set function has the following
- An empty object
ois bound to
Set.prototype. In other words, the prototype of
ois set to
- The function
Setis called with
ois bound to
- When the function returns, it returns
That is why methods of the object must be attached to the
Set.prototype object: it is the prototype of every instance of
I always found this pattern verbose and confusing. The
was made to be familiar for Java programmers, but there is no mention
of classes, only prototypes. In fact, it can be misleading to relate
to Java, since the receiver of a method is not bound statically in
The call to
add(2) has no explicit caller, so
this is bound to the
global object, which doesn’t have an
Also, functions have a unique
prototype property that is
unfortunately not the same thing as the
[[Proto]] property that
every object – including functions – have.
Luckily, there are alternatives to this pattern in vanilla JS. By not trying to mimic class-based languages, but harnessing the simplicity of the prototype model, one finds flexible and terse ways to create objects.
Attributes and methods are now just properties of the same object. In this respect, this form is more regular than the first pattern.
mkSet function puts out new objects of identical initial state.
Compared to the previous example, we have lost a bit of flexibility.
The bag was usable right away, but here we must call the factory to
create a new object before beginning to use it.
This form also has another downside: the methods are recreated
separately for each object. The
add method of
s2 are not
the same. Not only if this wasteful, but leaves us no way of adding
or altering methods for all objects of the same factory.
Sharing behavior with prototypes
We can correct the latter defect by putting the methods in a separate
setProto object. Then, in the factory method we must set the
[[Proto]] property to this
This way, all objects created by the factory
mkSet share the same
methods. We can easily extend the functionality of all sets by
appending methods to
Note that we did not correct the first defect: we must still use the
factory to begin using sets. However, we essentially recreated the
mkSet() does what
new Set() did, and
Set.prototype. It is less verbose, but now the methods and
constructor are separate entities, which makes even less sense. As a
setProto, is not a usable object on its own. We would like
to reunite the two.
Reuniting prototype and constructor
Since the constructor is just a function, why not put it inside the prototype object itself?
Set is still the prototype object, but the constructor is part of
its definition. Calling
Set.new() creates new objects inheriting
Set. In fact,
new is a method of all
While this is unusual for class-based languages like Java, where constructors are special methods, here we see they are just regular functions that return new objects.
There is a bit of redundancy there, namely the
Set in the line
__proto__: Set. Since we will call the constructor with
Set.new(), why not use
s1.new() are no longer equivalent. The latter
create an object that have
s1 as prototype, not
Set. This means
that we can specialize objects in branches, not only as a whole.
s11 all have the
clear method, but only
Prototypes as default objects
We should try to correct the first defect. As it stands, the only
method that makes sense to call on
new. Why not make
a fully functional instance of itself?
Just by calling
new() on the object defining the
set prototype, we
are giving it an
items array. Somehow we lost the direct reference
to the original prototype, but it does not matter since all
objects will be derived from
set itself (and we can still access it
Cloning is just another method
Finally, when creating objects we might want to make a deep copy of
an existing copy – not start from a fresh state. This can be
achieved by adding a
clone method that defines the deep cloning of
each attribute. It also binds
[[Proto]] of course.
s1 begins as an identical copy of
set, but not sharing the
reference to the
items array. If we want shallow cloning, we can
Object.create(set). And if we want to branch, we can call
set.new. We also can use
set right away as a set. All objects
set will inherit from changes made to their prototype,
which allow to specialize and extend their behavior as befit.