1

I am new to JavaScript and I was advised to read the book Learning JavaScript Design Patterns. I was reading the factory design pattern and came across the following difficulty, just have a look at the below script:

// Types.js - Constructors used behind the scenes

// A constructor for defining new cars
function Car( options ) {

  // some defaults
  this.doors = options.doors || 4;
  this.state = options.state || "brand new";
  this.color = options.color || "silver";

}

// A constructor for defining new trucks
function Truck( options){
  this.state = options.state || "used";
  this.wheelSize = options.wheelSize || "large";
  this.color = options.color || "blue";
}


// FactoryExample.js

// Define a skeleton vehicle factory
function VehicleFactory() {}

// Define the prototypes and utilities for this factory

// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;

// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function ( options ) {

  switch(options.vehicleType){
    case "car":
      this.vehicleClass = Car;
      break;
    case "truck":
      this.vehicleClass = Truck;
      break;
    //defaults to VehicleFactory.prototype.vehicleClass (Car)
  }

  return new this.vehicleClass( options );

};

// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
            vehicleType: "car",
            color: "yellow",
            doors: 6 });

// Test to confirm our car was created using the vehicleClass/prototype Car

// Outputs: true
console.log( car instanceof Car );

// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log( car );

This is the example I found for the factory design pattern, now my problem is understanding the following line. Considering that vehicleClass class is defined as a variable:

VehicleFactory.prototype.vehicleClass = Car;

I don't understand it being returned as a constructor function:

return new this.vehicleClass( options );

Well that's my only difficulty and running over the code again and again, still doesn't make sense to me.

1
  • A function is being passed in as a variable (functions are first-class citizens in JS), and then called later on. Commented Mar 17, 2015 at 7:59

4 Answers 4

1

What is returned here is not a function (you are right that it would not make sense). What is returned is a new object (see the new key word).

When you want to return a new object of type Truck than you would use the code:

return new Truck( options )

However in this specific case, the type of the vehicle you want is defined by the user. So this.vehicleClass refers to the value defined in the switch just above the return. In other words, vehicleClass will be replaced by Car (resp. Truck) if the users chose a car (resp. Truck).

The constructor that will be called will thus be either the Car or Truck constructor defined in the first lines of the code.

I hope it is more clear now.

Sign up to request clarification or add additional context in comments.

2 Comments

Ur freaking awesome !! Thanks
Thanks for marking my answer as valid, however I am not a Javascript expert (although I used it in a few occasions) and I think that the comments bellow should be read carefully. Especially concerning the fact that functions can be returned as objects in javascript. Hence although my answer might help to understand the problem it seems it is at least partially wrong when I state that "What is returned here is not a function".
1

What You are assigning to VehicleFactory.prototype.vehicleClass isn't an actual class, but a reference to a constructor function.

To create an object in JavaScript, the new operator must be followed by a constructor function. Since VehicleFactory.prototype.vehicleClass, or later in the code this.vehicleClass, holds a reference to a constructor function, you can create a new vehicle object with new this.vehicleClass(options).

One of the big advantages of JavaScript is the ability to pass functions as arguments, without the need of delegates.

I hope this makes it a little clearer.

Greetings

Comments

1

In Javascript Functions are objects, so they can be used as return values. This means that a function doesn’t need to return some sort of data value or array of data as a result of its execution.

A function can return another more specialized function, or it can create another function on-demand, depending on some inputs. Here’s a simple example: A function does some work, probably some one-off initialization, and then works on its return value. The returned value happens to be another function, which can also be executed:

var setup = function () {
   alert(1);
   return function () {
        alert(2);
   };
};
// using the setup function
var my = setup(); // alerts 1
my(); // alerts 2

Because setup() wraps the returned function, it creates a closure, and you can use this closure to store some private data, which is accessible by the returned function but not to the outside code. An example would be a counter that gives you an incremented value every time you call it:

var setup = function () {
    var count = 0;
    return function () {
        return (count += 1);
    };
};
// usage
var next = setup();
next(); // returns 1

Source: JavaScript Patterns By Stoyan Stefanov.

And in your case return new this.vehicleClass( options );, is returning a new object Calling the constructor vehicleClass .

Comments

1
new <expression>(<arguments>)

is evaluated like this:

  1. evaluate <expression>
  2. call new <result of 1>(<arguments>)

That is, you can put any expression between new and the opening parenthesis, as long as this expression returns a function.

BTW, this code doesn't seem to do things in a right way. First, it defines a property on the prototype:

VehicleFactory.prototype.vehicleClass = Car;

Then, in createVehicle, it adds a local property with the same name:

this.vehicleClass = Car;

which effectively overrides the prototype's vehicleClass in subsequent lookups:

 a = createVehicle(); // no `vehicleType` here, returns `Car`
 b = createVehicle({vehicleType: 'truck'}); // returns `Truck`
 c = createVehicle(); // no `vehicleType` here, but this time it returns `Truck`???

A better way to design createVehicle would be like this:

VehicleFactory.prototype.createVehicle = function ( options ) {
  var klass;    

  switch(options.vehicleType){
    case "car":
      klass = Car;
      break;
    case "truck":
      klass = Truck;
      break;
  }

  return new (klass || this.vehicleClass)( options );

};

On a subjective note, "design patterns" are not very idiomatic in javascript, if you want another reading recommendation, give JavaScript Allongé a try.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.