Listings 3-4 and 3-5 rely on this instanceof Marsupial returning false. The JavaScript instanceof operator inspects the prototype chain of the left-side operand, seeking the prototype of the right-side operand. If the right-side operand’s prototype is found, the left-side operand is
considered to be an instance of the right-side operand.
When a constructor function is executed with the new keyword, JavaScript creates a new empty object, links the new object’s prototype to the
prototype property of the constructor function, and executes the constructor function with this being the new object.
When new is omitted, none of those automatic steps occur. The
constructor function is not bound to a new object when executed; in Listings 3-4 and 3-5 it is bound to the global object. Additionally, the
prototype assignment doesn’t occur, and thus instanceof returns false.
Figure 3.1 shows the error that is output to the console when
var slider = Marsupial('Slider', true) is executed.
Figure 3.1
An alternative to throwing an error when new is not used is to automatically create an instance of an object using new and return that instead, as shown in Listing 3-5.
LISTING 3-5: Automatically create an instance with new (code filename: New Pattern\newpattern_03.js)
function Marsupial(name, nocturnal){
if(!(this instanceof Marsupial)){
return new Marsupial(name, nocturnal);
}
this.name = name;
this.isNocturnal = nocturnal;
}
var slider = Marsupial('Slider', true);
console.log(slider.name); // 'Slider'
Listing 3-5 executes the Marsupial function without using new, but instead of throwing an error as in Listing 3-4, a Marsupial is created with the new
keyword and returned. Using this mechanism, the programmer calling the function need not worry about whether Marsupial should be invoked with the
new keyword; new is automatically used if it has been omitted.
On the surface, the automatic use of the new keyword seems helpful, but
really all it does is allow the programmer to get away with mistakes. Consider the following:
var jester = Marsupial('Jester', false);
var merlin = new Marsupial('Merlin', false);
Both invocations of Marsupial produce an object as if they had been preceded by new even though only one of them is. If your team has adopted the
convention of naming constructor functions with an uppercase first letter, the creation of the jester instance appears to be incorrect.
Consistency begets reliability, and as such we prefer the protection
mechanism presented in Listing 3-4. Throwing an exception when new is omitted ensures that all Marsupial objects will be instantiated in the same way, contributing to a more consistent and reliable codebase. Additionally, when coupled with test-driven development, any exception generated via the omission of new will be identified immediately.
NOTE Automatic semicolon insertion, a JavaScript feature that also lets programmers get away with mistakes and allows for codebases to
become inconsistent, is considered by Douglas Crockford to be one of the awful parts of JavaScript that shouldn’t be relied upon (JavaScript: The Good Parts by Douglas Crockford, O’Reilly Media, 2008). Automatic new insertion, while perhaps not awful, should be avoided for the same
reasons.
The new object creation pattern also allows you to create function properties that are defined once and made available to all instances. Listing 3-6 shows how properties can be added to each instance of an object by defining them directly on the new object in the constructor function. The listing also
illustrates that each object instance has its own copy of the function.
LISTING 3-6: Adding a function directly to the new object (code filename: New Pattern\newpattern_04.js)
function Marsupial(name, nocturnal){
if(!(this instanceof Marsupial)){
throw new Error("This object must be created with new");
}
this.name = name;
this.isNocturnal = nocturnal;
// Each object instance gets its own copy of isAwake this.isAwake = function(isNight){
return isNight === this.isNocturnal;
} }
var maverick = new Marsupial('Maverick', true);
var slider = new Marsupial('Slider', false);
var isNightTime = true;
console.log(maverick.isAwake(isNightTime)); // true console.log(slider.isAwake(isNightTime)); // false // each object has its own isAwake function
console.log(maverick.isAwake === slider.isAwake); // false
Function properties may also be added to the constructor function’s
prototype. Defining functions on the prototype of the constructor function has the added benefit of limiting the number of copies of the function to one, reducing the memory footprint, and increasing performance when creating a large number of object instances.
Listing 3-7 shows how to add a function property to the constructor function’s prototype. The listing also illustrates that each object instance shares the
implementation of the function.
LISTING 3-7: Adding a function to the constructor function’s prototype (code filename: New
Pattern\newpattern_05.js)
function Marsupial(name, nocturnal){
if(!(this instanceof Marsupial)){
throw new Error("This object must be created with new");
}
this.name = name;
this.isNocturnal = nocturnal;
}
// Each object instance shares one copy of isAwake Marsupial.prototype.isAwake = function(isNight){
return isNight === this.isNocturnal;
}
var maverick = new Marsupial('Maverick', true);
var slider = new Marsupial('Slider', false);
var isNightTime = true;
console.log(maverick.isAwake(isNightTime)); // true console.log(slider.isAwake(isNightTime)); // false // the objects share a single instance of isAwake
console.log(maverick.isAwake === slider.isAwake); // true
To illustrate the performance gains that can be realized by utilizing the constructor function’s prototype, we’ve created a sample on
http://jsperf.com which pits the version of Marsupial that doesn’t use the
prototype against the version that does. You can run the comparison in your own browser by visiting http://jsperf.com/performance-prototype-vs-non- prototype, but to satisfy any immediate curiosity you have, Figure 3.2 shows the results of running the test in Chrome version 40 on OSX 10.10.
Figure 3.2
As Figure 3.2 shows, the version of Marsupial that uses the constructor’s
prototype to share a single copy of the isAwake function between all object instances is more than 90 percent faster than the version that creates a copy of the isAwake function for each object instance.
Table 3.4 summarizes how the new object creation pattern stacks up in terms of the SOLID and DRY principles.
Table 3.4 SOLID and DRY Summary for Objects Created with new PRINCIPLE RESULT
Single
Responsibility
Certainly possible, but it’s up to you to make sure that the objects you create are responsible for one thing, and one
thing only. The ability to inject dependencies into constructor functions helps with this.
Open/Closed Yes! The upcoming sections about inheritance illustrate how objects created with new may be extended.
Liskov
Substitution
Yes, through judicious use of inheritance
Interface Segregation
Yes, again through use of inheritance and other code-sharing patterns
Dependency Inversion
A resounding yes. Dependencies may be injected into constructor functions with ease.
Don’t Repeat Yourself
The new object creation pattern results in very DRY code.
Unfortunately, we haven’t found a good way to use AOP with this pattern. This is a significant disappointment because AOP would be handy to encapsulate new enforcement. The reason AOP and new don’t mix is that new creates an object that inherits from the prototype of the object being created. If that object has been wrapped with an aspect, then the aspect’s prototype, not the object’s, will be used. However, nothing prevents you from using AOP to decorate the functions of the
new’d object’s prototype.