Constructor function vs Factory functions

JavascriptOop

Javascript Problem Overview


Can someone clarify the difference between a constructor function and a factory function in Javascript.

When to use one instead of the other?

Javascript Solutions


Solution 1 - Javascript

The basic difference is that a constructor function is used with the new keyword (which causes JavaScript to automatically create a new object, set this within the function to that object, and return the object):

var objFromConstructor = new ConstructorFunction();

A factory function is called like a "regular" function:

var objFromFactory = factoryFunction();

But for it to be considered a "factory" it would need to return a new instance of some object: you wouldn't call it a "factory" function if it just returned a boolean or something. This does not happen automatically like with new, but it does allow more flexibility for some cases.

In a really simple example the functions referenced above might look something like this:

function ConstructorFunction() {
   this.someProp1 = "1";
   this.someProp2 = "2";
}
ConstructorFunction.prototype.someMethod = function() { /* whatever */ };

function factoryFunction() {
   var obj = {
      someProp1 : "1",
      someProp2 : "2",
      someMethod: function() { /* whatever */ }
   };
   // other code to manipulate obj in some way here
   return obj;
}

Of course you can make factory functions much more complicated than that simple example.

One advantage to factory functions is when the object to be returned could be of several different types depending on some parameter.

Solution 2 - Javascript

Benefits of using constructors

  • Most books teach you to use constructors and new

  • this refers to the new object

  • Some people like the way var myFoo = new Foo(); reads.

Drawbacks

  • Details of instantiation get leaked into the calling API (via the new requirement), so all callers are tightly coupled to the constructor implementation. If you ever need the additional flexibility of the factory, you'll have to refactor all callers (admittedly the exceptional case, rather than the rule).

  • Forgetting new is such a common bug, you should strongly consider adding a boilerplate check to ensure that the constructor is called correctly ( if (!(this instanceof Foo)) { return new Foo() } ). EDIT: Since ES6 (ES2015) you can't forget new with a class constructor, or the constructor will throw an error.

  • If you do the instanceof check, it leaves ambiguity as to whether or not new is required. In my opinion, it shouldn't be. You've effectively short circuited the new requirement, which means you could erase drawback #1. But then you've just got a factory function in all but name, with additional boilerplate, a capital letter, and less flexible this context.

Constructors break the Open / Closed Principle

But my main concern is that it violates the open/closed principle. You start out exporting a constructor, users start using the constructor, then down the road you realize you need the flexibility of a factory, instead (for instance, to switch the implementation to use object pools, or to instantiate across execution contexts, or to have more inheritance flexibility using prototypal OO).

You're stuck, though. You can't make the change without breaking all the code that calls your constructor with new. You can't switch to using object pools for performance gains, for instance.

Also, using constructors gives you a deceptive instanceof that doesn't work across execution contexts, and doesn't work if your constructor prototype gets swapped out. It will also fail if you start out returning this from your constructor, and then switch to exporting an arbitrary object, which you'd have to do to enable factory-like behavior in your constructor.

Benefits of using factories

  • Less code - no boilerplate required.

  • You can return any arbitrary object, and use any arbitrary prototype - giving you more flexibility to create various types of objects which implement the same API. For example, a media player that can create instances of both HTML5 and flash players, or an event library which can emit DOM events or web socket events. Factories can also instantiate objects across execution contexts, take advantage of object pools, and allow for more flexible prototypal inheritance models.

  • You'd never have a need to convert from a factory to a constructor, so refactoring will never be an issue.

  • No ambiguity about using new. Don't. (It will make this behave badly, see next point).

  • this behaves as it normally would - so you can use it to access the parent object (for example, inside player.create(), this refers to player, just like any other method invocation would. call and apply also reassign this, as expected. If you store prototypes on the parent object, that can be a great way to dynamically swap out functionality, and enable very flexible polymorphism for your object instantiation.

  • No ambiguity about whether or not to capitalize. Don't. Lint tools will complain, and then you'll be tempted to try to use new, and then you'll undo the benefit described above.

  • Some people like the way var myFoo = foo(); or var myFoo = foo.create(); reads.

Drawbacks

  • new doesn't behave as expected (see above). Solution: don't use it.

  • this doesn't refer to the new object (instead, if the constructor is invoked with dot notation or square bracket notation, e.g. foo.bar() - this refers to foo - just like every other JavaScript method -- see benefits).

Solution 3 - Javascript

A constructor returns an instance of the class you call it on. A factory function can return anything. You would use a factory function when you need to return arbitrary values or when a class has a large setup process.

Solution 4 - Javascript

A Constructor function example

function User(name) {
  this.name = name;
  this.isAdmin = false;
}

let user = new User("Jack");
  • new creates an object prototyped on User.prototype and calls User with the created object as its this value.

  • new treats an argument expression for its operand as optional:

         let user = new User;
    

    would cause new to call User with no arguments.

  • new returns the object it created, unless the constructor returns an object value, which is returned instead. This is an edge case which for the most part can be ignored.

Pros and Cons

Objects created by constructor functions inherit properties from the constructor's prototype property, and return true using the instanceOf operator on the constructor function.

The above behaviors can fail if you dynamically change the value of the constructor's prototype property after having already used the constructor. Doing so is rare, and it can't be changed if the constructor were created using the class keyword.

Constructor functions can be extended using the extends keyword.

Constructor functions can't return null as an error value. Since it's not an object data type, it is ignored by new.

A Factory function example

function User(name, age) {
  return {
    name,
    age,
  }
};

let user = User("Tom", 23);

Here the factory function is called without new. The function is entirely responsible for the direct or indirect use if its arguments and the type of object it returns. In this example it returns a simple [Object object] with some properties set from arguments.

Pros and Cons

Easily hides the implementation complexities of object creation from the caller. This is particularly useful for native code functions in a browser.

The factory function need not always return objects of the same type, and could even return null as an error indicator.

In simple cases, factory functions can be simple in structure and meaning.

Objects returned do not generally inherit from the factory function's prototype property, and return false from instanceOf factoryFunction.

The factory function can't be safely extended using the extends keyword because extended objects would inherit from the factory functions prototype property instead of from the prototype property of the constructor used by the factory function.

Solution 5 - Javascript

Factories are "always" better. When using object orientated languages then

  1. decide on the contract (the methods and what they will do)
  2. Create interfaces that expose those methods (in javascript you don't have interfaces so you need to come up with some way of checking the implementation)
  3. Create a factory that returns an implementation of each interface required.

The implementations (the actual objects created with new) are not exposed to the factory user/consumer. This means that the factory developer can expand and create new implementations as long as he/she doesn't break the contract...and it allows for the factory consumer to just benefit from the new API without having to change their code...if they used new and a "new" implementation comes along then they have to go and change every line which uses "new" to use the "new" implementation...with the factory their code doesn't change...

Factories - better than all anything else - the spring framework is completely built around this idea.

Solution 6 - Javascript

Factories are a layer of abstraction, and like all abstractions they have a.cost in complexity. When encountering a factory based API figuring out what the factory is for a given API can be challenging for the API consumer. With constructors discoverability is trivial.

When deciding between ctors and factories you need to decide if the complexity is justified by the benefit.

Worth noting that Javascript constructors can be arbitrary factories by returning something other than this or undefined. So in js you can get the best of both worlds - discoverable API and object pooling/caching.

Solution 7 - Javascript

For the differences, Eric Elliott clarified very well,

But for the second question:

> When to use one instead of the other?

If you are coming from the object-oriented background, Constructor function looks more natural to you. this way you shouldn't forget to use new keyword.

Solution 8 - Javascript

one new thing to add for previous answers is that there is a new way to declare Constructor functions.

class CreateCirle {
    constructor(radius) {
        this.radius = radius;
        this.draw = function () {
            console.log('we draw a circle with radius: ', this.radius);
        };
        this.name = "Circly";
        this.address = "Math Graph";
    }
}

you have to add class and constructor keyword.

then invoke it normally with no difference.

let cirle = new CreateCirle(5);
cirle.draw();

Don't forget to make it with PascalCase name convention as mentioned before.

Solution 9 - Javascript

  • I think the factory function is superior to the constructor function. Using new with the constructor function, we are binding our code to one specific way of creating an object, while with a factory, we are free so we can create more different instances without binding ourselves. Let's say we have this class:

    const file = new CreateFile(name)

If we want to refactor CreateFile class, creating subclasses for the file format our server supports, we can write an elegan factory function:

function CreateFile(name) {
  if (name.match(/\.pdf$/)) {
    return new FilePdf(name);
  } else if (name.match(/\.txt$/)) {
    return new FileTxt(name);
  } else if (name.match(/\.md$/)) {
    return new FileMd(name);
  } else {
    throw new Error("Not supprted file type");
  }
}
  • with factory functions, we can implement private variables, hide the information from the users which is called encapsulation.

    function createPerson(name) {
      const privateInfo = {};
      // we create person object
      const person = {
        setName(name) {
          if (!name) {
            throw new Error("A person must have a name");
          }
         privateInfo.name = name;
        },
         getName() {
           return privateInfo.name;
        },
     };
       person.setName(name);
       return person;
    }
    

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSinanView Question on Stackoverflow
Solution 1 - JavascriptnnnnnnView Answer on Stackoverflow
Solution 2 - JavascriptEric ElliottView Answer on Stackoverflow
Solution 3 - JavascriptIgnacio Vazquez-AbramsView Answer on Stackoverflow
Solution 4 - JavascripttraktorView Answer on Stackoverflow
Solution 5 - JavascriptColin SaxtonView Answer on Stackoverflow
Solution 6 - JavascriptPeterHView Answer on Stackoverflow
Solution 7 - JavascriptMostafaView Answer on Stackoverflow
Solution 8 - JavascriptMohamed S.ShelfView Answer on Stackoverflow
Solution 9 - JavascriptYilmazView Answer on Stackoverflow