This post explores exemplars (factories for objects) and how to implement them. The term exemplar has been proposed by Allen Wirfs-Brock to avoid the term class, which is not a good fit for JavaScript: Exemplars are similar to classes, but they are not classes.

Exemplars

Regular expression literals: produce instances of RegExp : > /abc/ instanceof RegExp true

: Array initializers: produce instances of Array . > [1, 2, 3] instanceof Array true

. Object initializers: look as follows. var jane = { name: "Jane", describe: function () { return "Person called "+this.name; } } Each object produced by an object initializer (without non-standard extensions) is a direct instance of Object : > jane instanceof Object true

Function exemplars (constructors)

new

jane

function Person(name) { this.name = name; } Person.prototype.describe = function () { return "Person called "+this.name; };

new

var jane = new Person("Jane");

jane

Person

instanceof

> jane instanceof Person true

Person

> jane.name 'Jane'

Person.prototype

> jane.describe() 'Person called Jane' > jane.describe === Person.prototype.describe true

Person.prototype

Person

> Person.prototype.isPrototypeOf(jane) true

jane instanceof Person

Person.prototype.isPrototypeOf(jane)

Optional parameters

function Point(x, y) { if (arguments.length >= 2) { this.x = x; this.y = y; } else { this.x = 0; this.y = 0; } }

function ColorPoint(x, y, color) { if (arguments.length >= 3) { this.color = color; } else { this.color = black; } if (arguments.length >= 2) { this.x = x; this.y = y; } else { this.x = 0; this.y = 0; } }

undefined

this.x = (x !== undefined ? x : 0); // this.x = (... ? value : default)

function valueOrDefault(value, theDefault) { return (value !== undefined && value !== null ? value : theDefault); }

ColorPoint

function ColorPoint(x, y, color) { this.x = valueOrDefault(x, 0); this.y = valueOrDefault(y, 0); this.color = valueOrDefault(color, "black"); }

||

function ColorPoint(x, y, color) { this.x = x || 0; this.y = y || 0; this.color = color || "black"; }

left || right

left

Boolean(left)

right

||

undefined , null

, false

+0 , -0 , NaN

, , ""

color

> "" || "black" 'black'

Option objects

undefined

null

ColorPoint

> new ColorPoint({ color: "black" }) { color: 'black', x: 0, y: 0 } > new ColorPoint({ x: 33 }) { x: 33, y: 0, color: 'black' } > new ColorPoint() { x: 0, y: 0, color: 'black' }

function ColorPoint(options) { _.defaults(this, options, { x: 0, y: 0, color: "black" }); }

options

this

Chainable setters

> new ColorPoint().setX(12).setY(7) { x: 12, y: 7, color: 'black' } > new ColorPoint().setColor("red") { x: 0, y: 0, color: 'red' }

this

ColorPoint

function ColorPoint() { // Set default values this.x = 0; this.y = 0; this.color = "black"; } ColorPoint.prototype.setX = function (x) { this.x = x; return this; } ColorPoint.prototype.setY = function (y) { this.y = y; return this; } ColorPoint.prototype.setColor = function (color) { this.color = color; return this; }

withSetters

var ColorPoint = function (x, y, color) { this.x = 0; this.y = 0; this.color = "black"; }.withSetters( "x", "y", "color" );

withSetters

Function.prototype.withSetters = function (/*setter names*/) { var Constr = this; Array.prototype.forEach.call(arguments, function (propName) { var capitalized = propName[0].toUpperCase() + propName.slice(1); var setterName = "set" + capitalized; Constr.prototype[setterName] = function (value) { this[propName] = value; return this; }; }); return this; };

var ColorPoint = function (x, y, color) { this.applyDefaults(); }.withSetters({ x: 0, y: 0, color: "black" });

ColorPoint

applyDefaults

Function.prototype.withSetters = function (props) { var Constr = this; Constr.prototype.applyDefaults = function () { _.defaults(this, props); } Object.keys(props).forEach(function (propName) { var capitalized = propName[0].toUpperCase() + propName.slice(1); var setterName = "set" + capitalized; Constr.prototype[setterName] = function (value) { this[propName] = value; return this; }; }); return this; };

Initialization methods

Array

> new Array(3) [ , , ]

> new Array("a", "b", "c") [ 'a', 'b', 'c' ]

Angle

> new Angle().initDegrees(180).toString() '3.141592653589793rad'

new Angle()

initDegrees()

Angle

function Angle() { } Angle.prototype.initRadians = function (rad) { this.rad = rad; return this; }; Angle.prototype.initDegrees = function (deg) { this.rad = deg * Math.PI / 180; return this; }; Angle.prototype.toString = function () { return this.rad+"rad"; };

initRadians

initDegrees

_initialized

function Angle() { this._initialized = false; } Angle.prototype._forbidInitialized = function () { if (this._initialized) { throw new Error("Already initialized"); } this._initialized = true; }; Angle.prototype.initRadians = function (rad) { this._forbidInitialized(); this.rad = rad; return this; }; Angle.prototype.initDegrees = function (deg) { this._forbidInitialized(); this.rad = deg * Math.PI / 180; return this; }; Angle.prototype._forceInitialized = function () { if (!this._initialized) { throw new Error("Not initialized"); } }; Angle.prototype.toString = function () { this._forceInitialized(); return this.rad+"rad"; };

Static factory methods

> var a = Angle.withDegrees(180); > a.toString() '3.141592653589793rad' > a instanceof Angle true

function Angle(rad) { this.rad = rad; } Angle.withRadians = function (rad) { return new Angle(rad); }; Angle.withDegrees = function (deg) { return new Angle(deg * Math.PI / 180); }; Angle.prototype.toString = function () { return this.rad+"rad"; };

new Angle()

Angle

Angle

instanceof

Anis a construct that creates objects. Each of the following kinds of exemplars produces instances of a single fixed type:If you need more flexibility you can turn to(Sect. 2) and(Sect. 4) where you can specify the type of the instances per exemplar.A function can be used as an exemplar if invoked via theoperator. It becomes a so-called. We have previously seen how to create the single “person”via an object initializer. The following constructor is an exemplar for this kind of object.This is how you use theoperator to create an object:is considered in instance of. You can check that relationship via theoperator:There are two parts for setting up an instance of: The instance-specific properties are added via the constructor:Properties that are shared by all instances (mostly methods) are inherited fromYou can verify that the single objectis indeed the prototype of all instances ofThe prototype-of relationship is also used to check whether an object is an instance of a constructor. The expressionis actually implemented asOften, one would like the same constructor to set up its instances in different ways. The following constructor for two-dimensional points can be invoked with either zero arguments or two arguments. In the former case, it produces a point (0,0), in the latter case, one specifies x coordinate and y coordinate.This idea can be extended to more than two possible argument combinations. For example, the following constructor for points with a color can receive 3 arguments, 2 arguments, or 0 arguments.There is a possibility to write this code more compactly: Missing parameters arein JavaScript which means that we want to make assignments likeThe following function implements this kind of check:Nowis more compact:You can also use theoperator:Explanation:evaluates toifis true and to, otherwise. Caveat: With, the following values will all be interpreted as the argument being missing:You therefore cannot assign the empty string to, becausePositional optional parameters are limiting with regard to what parameters can be omitted: If a parameter that you want to omit isn’t at the end, you have to insert a placeholder value such asor. Furthermore, if there are many parameters, you quickly lose sight of the meaning of each one. The idea of anis to provide optional parameters as an object that is created via an object initializer. Forthat would look as follows.With a helper function, it is remarkably easy to transfer the optional values to the instance under construction and to fill in defaults for missing properties:Above we have used the function defaults of the Underscore library [1] . It copies all of the properties oftothat don’t exist there, yet. And similarly fills in the defaults via the third argument. [2] has more information on option objects.One can achieve a style that is very similar to an option object by creating a setter for each optional parameter. That looks as follows:Naturally, each of the setters must return, so that setters can be chained and that the final result of such a chain is always the instance ofGiven how mechanical writing such setters is, we can write a methodthat does it for us:is a method that, if applied to a function, adds setters to that function’s prototype:Refining this approach, we can use an object initializer to specify both setter names and default values at the same time:The defaults are added to the fresh instance ofvia the method. Apart from that method, not much changes – now the setter names are extracted from an object.Sometimes there are several mutually exclusive “modes” or ways of initializing an object. Using the same constructor for all modes is tricky. It would be better to have a clear indication of which mode is active. The problem of using the same name for different operations is nicely illustrated by a well-known quirk of JavaScript’sconstructor. The first operation is to create an empty array with the given length:The second operation is to create an array with given elements:However, the second operation is problematic, because if you want to create an array that has a single natural number in it, operation 1 takes over and doesn’t let you do it. A better solution is to again use chainable setters and give each operation a different name. As an example, let’s look at a constructorwhose instances can be initialized in either degrees or radians.Note how we have separated instantiation (instance creation) viafrom initialization via. Constructors usually perform both tasks; here we have delegated the latter task to a method.can be implemented as follows.andare not really setters, they are. Initialization methods differ from setters in two ways: First, they can have more than one parameter or no parameter. In contrast, setters usually have exactly one parameter. Second, you often want to ensure that if a method is invoked on an instance, that instance has been initialized beforehand. And, possibly, one shouldn’t initialize more than once. The following code uses the boolean flagto perform these two checks.are an alternative to initialization methods. They are methods of the constructor that create instances. Given that a constructor is similar to a class in class-based languages, such methods are often called. They are called, because they produce instances. Creating an angle with a static factory method instead of a constructor and an initialization method looks as follows.This is an implementation:The point of factory methods is to replace the constructor. We therefore want to make it impossible to accidentally invoke, or even justas a function. Simply hidingwon’t do, because we need it for

Guarding the constructor, approach 1. The constructor throws an error if it isn’t called with a value that is only known to the factory methods. We keep the value secret by putting it inside an immediately-invoked function expression (IIFE, [3]).

var Angle = function () { var constrGuard = {}; function Angle(guard, rad) { if (guard !== constrGuard) { throw new Error("Must use a factory method"); } this.rad = rad; } Angle.withRadians = function (rad) { return new Angle(constrGuard, rad); }; Angle.withDegrees = function (deg) { return new Angle(constrGuard, deg * Math.PI / 180); }; Angle.prototype.toString = function () { return this.rad+"rad"; }; return Angle; }();

Angle

Object.create(Angle.prototype)

createInstance

var Angle = function () { function Angle() { throw new Error("Must use a factory method"); } function createInstance(rad) { var inst = Object.create(Angle.prototype); inst.rad = rad; return inst; } Angle.withRadians = function (rad) { return createInstance(rad); }; Angle.withDegrees = function (deg) { return createInstance(deg * Math.PI / 180); }; Angle.prototype.toString = function () { return this.rad+"rad"; }; return Angle; }();

> var a = Angle.withDegrees(180); > a.toString() '3.141592653589793rad' > a instanceof Angle true

Creating instances of multiple types in a single location

Expression ├── Addition └── Integer

Expression

Integer

Addition

Expression

Addition

Integer

Expression.parse = function (str) { if (/^[-+]?[0-9]+$/.test(str)) { return new Integer(str); } ... }

new C() instanceof C

> function C() { return {} } > new C() instanceof C false

function Expression(str) { if (/^[-+]?[0-9]+$/.test(str)) { return new Integer(str); } ... }

new Expression("-125") instanceof Integer

this

The new operator

Invocation

new <function-valued expression>(arg1, arg2, ...)

var inst = new Constr(); var inst = new Constr;

any expression, in parentheses, that evaluates to a function an identifier, optionally followed by one or more property accesses

new

foo()

function foo() { function bar(arg) { this.arg = arg; } return bar; }

foo()

foo()("hello")

new

> new (foo())("hello") { arg: 'hello' }

foo()

foo

"hello"

new

function Color(name) { this.name = name; } Color.prototype.toString = function () { return "Color("+this.name+")"; }

> new Color("green").toString() 'Color(green)'

(new Color("green")).toString()

new

var namespace = {}; namespace.Color = function (name) { this.name = name; };

> new namespace.Color("red") { name: 'red' }

The new operator ignores a bound value for `this`

bind()

this

n

var obj = {}; function F() { console.log("this === obj? " + (this === obj)); }

bind

var boundF = F.bind(obj);

boundF

> boundF() this === obj? true undefined

boundF()

undefined

new

this

> new boundF() instanceof F this === obj? false true

> function Constr() { console.log(this === obj) } undefined > (Constr.bind(obj))() true undefined > new (Constr.bind(obj))() false {}

The new operator doesn’t work with apply()

Date

new Date(2011, 11, 24)

> new Date.apply(null, [2011, 11, 24]) TypeError: function apply() { [native code] } is not a constructor

new

Date.apply

Object exemplars

function Person(name) { this.name = name; } Person.prototype.describe = function () { return "Person called "+this.name; };

new

> new Person("Jane") instanceof Person true

> Object.create(Person.prototype) instanceof Person true

Object.create()

Person.prototype

Person

An alternative is to not use the constructor to create an instance of, butThe factory methods now use the functionthat implements this approach.Interaction:Sometimes, there is a hierarchy of types and you want to create instances of those types in a single location. For example, expressions:The typehas two subtypes:and(whose operands can be expressions). Assuming thatis abstract and will never be instantiated, parsing a text string will produce an instance of eitheror. The common way to implement such an operation is via a factory method:However, JavaScript also lets you return an arbitrary object from a constructor. Hence, while the following assertion usually holds:it doesn’t have to:That allows you to implement expression parsing as follows:Then the following assertion holds:Most JavaScript engines perform an automatic optimiziation and don’t create an instance of the constructor, ifisn’t accessed and another object is (explicitly) returned.Theabove is usually either#1 matters when you want to applyto the result of a function, e.g. the following functionCalling the result ofas a function is easy, you simply append parentheses with arguments:To use the result as the operand of, you have to add additional parentheses:Without parentheses around, you would invokeas a constructor and then call the result as a function, with the argument. That the operand ofis finished with the first parentheses is an advantage when it comes to invoking a method on a newly created object. As an example, consider a constructor for colors.You can create a color and then immediately invoke a method on it:The above expression is equivalent toHowever, property accesses before the parentheses are considered part of the operand of. That is handy when you want to put a constructor inside an object:Interaction:Functions have a methodthat lets you create a new function with a fixed value for, whose first 0 toparameters are already filled in. Given the definitions.Then you can create a new function viaworks as expected when called as a function:The result ofis. However,overrides the boundwith a new instance:You can call theconstructor as follows.If you want to provide the arguments via an array, you cannot use apply:The reason is obvious:expectsto be a constructor. More work is required to achieve for constructors what apply achieves for functions [4] One normally usesto create objects:An instance is created via theoperator:ECMAScript 5 introduced a new way of creating instances:The above invocation ofproduced an object whose prototype is. There are two things that can be improved: First, we can get rid of the function exemplarand work directly with the the prototype object. Second, we would like to initialize a newly created instance in the same manner that constructors do.

Working directly with the prototype object. The prototype object is now our exemplar, we want it to bear the name Person , so that we can discard the constructor. This looks as follows.

var Person = { describe: function () { return "Person called "+this.name; } };

instanceof

isPrototypeOf()

> Person.isPrototypeOf(Object.create(Person)) true

Person

init()

var Person = { init: function (name) { this.name = name; return this; }, describe: function () { return "Person called "+this.name; } };

init()

> var jane = Object.create(Person).init("Jane"); > Person.isPrototypeOf(jane) true > jane.describe() 'Person called Jane'

Topics not covered by this post

Subtyping [6] and how to subtype JavaScript’s built-ins [7].

Keeping instance data private. You can read “Private data for objects in JavaScript” for more information.

References

We can’t use, any more, because it is an operator designed to work with function exemplars. However,works nicely:We still haven’t initialized our instances of. To do so, we introduce a method calledNote how similaris to a constructor. We use this object exemplar as follows:You can consult [5] for more information on object exemplars, including a library for working with them.Two topics that are relevant to object creation were not covered by this post: