Factory vs Constructor Functions

In JavaScript, any function can return a new object. When it’s not a constructor function or class, it’s called a factory function.

Eric Elliot

Two fundamental paradigms stand out in the realm of Javascript fundamentals: factory functions and constructor functions. These two techniques serve as the building blocks for creating objects, each with its own advantages and use cases. In this exploration, I will delve into the world of factory and constructor functions, dissecting their differences, strengths, and when to choose one over the other. Understanding these concepts will undoubtedly enhance your ability to design robust and maintainable code.

TL;DR: The General Purpose of them both is to create your object creation logic once and use either function to create multiple objects. All the credit goes to Sina for his insightful presentation of the concept on his channel, ColorCode.

Factory Function

It creates and returns an object.

function personFactory(n) {
  return { name: n }
}

const me = personFactory('Behnam')

One thing to note here is that here, we are not really using an inheritance hierarchy.

function createPerson(name) {
  return {
    // name: name, // full version
    name, // shorthand version

    talk() {
      return `I am ${this.name}`;
    },
  };
}

const me = createPerson("Behnam");
const you = createPerson("Qoli");

console.log(me); // An instance of the Obj. --> {name: 'Behnam', talk: f}
console.log(you); // An instance of the Obj. --> {name: 'Qoli', talk: f}

me.talk = function () {
  return `Hello, my name is ${this.name}`;
};

console.log(me.talk()); // Hello, my name is Behnam
console.log(you.talk()); // I am Qoli

The Problem with Factory Functions

As mentioned above, we are not really using an inheritance hierarchy.

So the two instances are not pointing to the same thing; they are DIFFERENT.

The Workaround

The Bad Approach

Here is a noteworthy, yet very BAD approach:

Object.prototype.speak = function() { // every object below it will have this speak in their proto
	return 'I can speak'
}

// So, these are all valid and available:
me.speak();
you.speak();

In other words, every object in your application has that method in it! Even if you define it after the fact, like below:

const a = {}; // new object

a.speak(); // Exists! And returns 'I can speak'
window.speak(); // Also exists! BAD IDEA

The Good Approach

Now back to solving the original problem of inheritance, as introduced above. Notice we will return something other than an empty object {}: using Object.create()

const myCoolProto = {
	talk() {
		return `Hello, I am ${this.name}`
	}
}

function createPerson(name) {
	return Object.create(myCoolProto, { // The API of Object.create is a bit different
		name: {
			value: name
		}
	})
}

const me = createPerson("Behnam")

Here is the output:

const myCoolProto = {
  talk() {
    return `Hello, I am ${this.name}`;
  },
};

function createPerson(name) {
  return Object.create(myCoolProto, {
    // The API of Object.create is a bit different
    name: {
      value: name,
    },
  });
}

const me = createPerson("Behnam");
console.log(me);

console.log(me.talk()); // Hello, I am Behnam

Constructor Function

Conventionally, they start with a Capital letter and you call it with a new keyword.

function Person(name) {
	this.name = name
}

const me = new Person('Behnam') // aka an Obj. instantiation

Factory vs Constructor Functions

Below, notice the difference in what they return.

function newPerson(name) { // Factory Fn
  return {
    name: name,
  };
}

const bob = newPerson("Bobby"); // {name: 'Bobby'}

console.log(bob);

function Person(name) { // Constructor Fn
  this.name = name;
}

const me = new Person("Behnam");

console.log(me); // Person {name: Behnam}

NOW WE ARE INHERITING. It means that our Constructor function already comes with its’ OWN PROTOTYPE. In other words, Behnam is now inheriting from Person.

So we can do something like this:

Person.prototype.talk = function () {
  return `Hello, I am ${this.name}`;
};

console.log(me.talk()); // Hello, I am Behnam

const sam = new Person("Sam");
console.log(sam.talk()); // Hello, I am Sam

Summary

A few key facts about Factory Functions:

  • They are just a function.
  • They are a little Simpler.
  • There is no new keyword involved, so there is no this, therefore, they are just simpler.
  • You just return an object!
  • A little more flexible, you are really using the power of closure in a Factory. (aka Data Privacy) See the example below
function createPerson(name) {
  return {
    talk() {
      return `${name}`;
    },
  };
}

const me = createPerson("Behnam");
console.log(me); // only the talk function
console.log(me.talk()); // Behnam
console.log(me.name); // undefined - in other words, it is safe!
// This is what we call data privacy
Share
Factory vs Constructor Functions