> /abc/ instanceof RegExp true
> [1, 2, 3] instanceof Array true
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 Person(name) { this.name = name; } Person.prototype.describe = function () { return "Person called "+this.name; };This is how you use the new operator to create an object:
var jane = new Person("Jane");jane is considered in instance of Person. You can check that relationship via the instanceof operator:
> jane instanceof Person trueThere are two parts for setting up an instance of Person: The instance-specific properties are added via the constructor:
> jane.name 'Jane'Properties that are shared by all instances (mostly methods) are inherited from Person.prototype:
> jane.describe() 'Person called Jane' > jane.describe === Person.prototype.describe trueYou can verify that the single object Person.prototype is indeed the prototype of all instances of Person:
> Person.prototype.isPrototypeOf(jane) trueThe prototype-of relationship is also used to check whether an object is an instance of a constructor. The expression
jane instanceof Personis actually implemented as
Person.prototype.isPrototypeOf(jane)
function Point(x, y) { if (arguments.length >= 2) { this.x = x; this.y = y; } else { this.x = 0; this.y = 0; } }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.
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; } }There is a possibility to write this code more compactly: Missing parameters are undefined in JavaScript which means that we want to make assignments like
this.x = (x !== undefined ? x : 0); // this.x = (... ? value : default)The following function implements this kind of check:
function valueOrDefault(value, theDefault) { return (value !== undefined && value !== null ? value : theDefault); }Now ColorPoint is more compact:
function ColorPoint(x, y, color) { this.x = valueOrDefault(x, 0); this.y = valueOrDefault(y, 0); this.color = valueOrDefault(color, "black"); }You can also use the || operator:
function ColorPoint(x, y, color) { this.x = x || 0; this.y = y || 0; this.color = color || "black"; }Explanation:
left || rightevaluates to left if Boolean(left) is true and to right, otherwise. Caveat: With ||, the following values will all be interpreted as the argument being missing:
> "" || "black" 'black'
> 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' }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:
function ColorPoint(options) { _.defaults(this, options, { x: 0, y: 0, color: "black" }); }Above we have used the function defaults of the Underscore library [1]. It copies all of the properties of options to this that don’t exist there, yet. And similarly fills in the defaults via the third argument. [2] has more information on option objects.
> new ColorPoint().setX(12).setY(7) { x: 12, y: 7, color: 'black' } > new ColorPoint().setColor("red") { x: 0, y: 0, color: 'red' }Naturally, each of the setters must return this, so that setters can be chained and that the final result of such a chain is always the instance of 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; }Given how mechanical writing such setters is, we can write a method withSetters that does it for us:
var ColorPoint = function (x, y, color) { this.x = 0; this.y = 0; this.color = "black"; }.withSetters( "x", "y", "color" );withSetters is a method that, if applied to a function, adds setters to that function’s prototype:
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; };Refining this approach, we can use an object initializer to specify both setter names and default values at the same time:
var ColorPoint = function (x, y, color) { this.applyDefaults(); }.withSetters({ x: 0, y: 0, color: "black" });The defaults are added to the fresh instance of ColorPoint via the method applyDefaults. Apart from that method, not much changes – now the setter names are extracted from an object.
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; };
> new Array(3) [ , , ]The second operation is to create an array with given elements:
> new Array("a", "b", "c") [ 'a', 'b', 'c' ]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 constructor Angle whose instances can be initialized in either degrees or radians.
> new Angle().initDegrees(180).toString() '3.141592653589793rad'Note how we have separated instantiation (instance creation) via new Angle() from initialization via initDegrees(). Constructors usually perform both tasks; here we have delegated the latter task to a method. Angle can be implemented as follows.
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 and initDegrees are not really setters, they are initialization methods. 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 flag _initialized to perform these two checks.
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"; };
> var a = Angle.withDegrees(180); > a.toString() '3.141592653589793rad' > a instanceof Angle trueThis is an implementation:
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"; };The point of factory methods is to replace the constructor. We therefore want to make it impossible to accidentally invoke new Angle(), or even just Angle as a function. Simply hiding Angle won’t do, because we need it for instanceof.
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; }();Guarding the constructor, approach 2. An alternative is to not use the constructor to create an instance of Angle, but
Object.create(Angle.prototype)The factory methods now use the function createInstance that implements this approach.
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; }();Interaction:
> var a = Angle.withDegrees(180); > a.toString() '3.141592653589793rad' > a instanceof Angle true
Expression ├── Addition └── IntegerThe type Expression has two subtypes: Integer and Addition (whose operands can be expressions). Assuming that Expression is abstract and will never be instantiated, parsing a text string will produce an instance of either Addition or Integer. The common way to implement such an operation is via a factory method:
Expression.parse = function (str) { if (/^[-+]?[0-9]+$/.test(str)) { return new Integer(str); } ... }However, JavaScript also lets you return an arbitrary object from a constructor. Hence, while the following assertion usually holds:
new C() instanceof Cit doesn’t have to:
> function C() { return {} } > new C() instanceof C falseThat allows you to implement expression parsing as follows:
function Expression(str) { if (/^[-+]?[0-9]+$/.test(str)) { return new Integer(str); } ... }Then the following assertion holds:
new Expression("-125") instanceof IntegerMost JavaScript engines perform an automatic optimiziation and don’t create an instance of the constructor, if this isn’t accessed and another object is (explicitly) returned.
new <function-valued expression>(arg1, arg2, ...)
var inst = new Constr(); var inst = new Constr;The function-valued expression above is usually either
function foo() { function bar(arg) { this.arg = arg; } return bar; }Calling the result of foo() as a function is easy, you simply append parentheses with arguments:
foo()("hello")To use the result as the operand of new, you have to add additional parentheses:
> new (foo())("hello") { arg: 'hello' }Without parentheses around foo(), you would invoke foo as a constructor and then call the result as a function, with the argument "hello". That the operand of new is 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.
function Color(name) { this.name = name; } Color.prototype.toString = function () { return "Color("+this.name+")"; }You can create a color and then immediately invoke a method on it:
> new Color("green").toString() 'Color(green)'The above expression is equivalent to
(new Color("green")).toString()However, property accesses before the parentheses are considered part of the operand of new. That is handy when you want to put a constructor inside an object:
var namespace = {}; namespace.Color = function (name) { this.name = name; };Interaction:
> new namespace.Color("red") { name: 'red' }
var obj = {}; function F() { console.log("this === obj? " + (this === obj)); }Then you can create a new function via bind:
var boundF = F.bind(obj);boundF works as expected when called as a function:
> boundF() this === obj? true undefinedThe result of boundF() is undefined. However, new overrides the bound this with a new instance:
> 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 {}
new Date(2011, 11, 24)If you want to provide the arguments via an array, you cannot use apply:
> new Date.apply(null, [2011, 11, 24]) TypeError: function apply() { [native code] } is not a constructorThe reason is obvious: new expects Date.apply to be a constructor. More work is required to achieve for constructors what apply achieves for functions [4].
function Person(name) { this.name = name; } Person.prototype.describe = function () { return "Person called "+this.name; };An instance is created via the new operator:
> new Person("Jane") instanceof Person trueECMAScript 5 introduced a new way of creating instances:
> Object.create(Person.prototype) instanceof Person trueThe above invocation of Object.create() produced an object whose prototype is Person.prototype. There are two things that can be improved: First, we can get rid of the function exemplar Person and 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; } };We can’t use instanceof, any more, because it is an operator designed to work with function exemplars. However, isPrototypeOf() works nicely:
> Person.isPrototypeOf(Object.create(Person)) trueInitialization. We still haven’t initialized our instances of Person. To do so, we introduce a method called init().
var Person = { init: function (name) { this.name = name; return this; }, describe: function () { return "Person called "+this.name; } };Note how similar init() is to a constructor. We use this object exemplar as follows:
> var jane = Object.create(Person).init("Jane"); > Person.isPrototypeOf(jane) true > jane.describe() 'Person called Jane'You can consult [5] for more information on object exemplars, including a library for working with them.