Lets say I want to create an object that represents a person with the properties name, age, and gender.
Is there any objective reason I should use a class to represent this rather than just making a function `Person()` which creates the object?
Videos
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.
Benefits of using constructors
Most books teach you to use constructors and
newthisrefers to the new objectSome people like the way
var myFoo = new Foo();reads.
Drawbacks
Details of instantiation get leaked into the calling API (via the
newrequirement), 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
newis 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 forgetnewwith aclassconstructor, or the constructor will throw an error.If you do the
instanceofcheck, it leaves ambiguity as to whether or notnewis required. In my opinion, it shouldn't be. You've effectively short circuited thenewrequirement, 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 flexiblethiscontext.
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 makethisbehave badly, see next point).thisbehaves as it normally would - so you can use it to access the parent object (for example, insideplayer.create(),thisrefers toplayer, just like any other method invocation would.callandapplyalso reassignthis, 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();orvar myFoo = foo.create();reads.
Drawbacks
newdoesn't behave as expected (see above). Solution: don't use it.thisdoesn't refer to the new object (instead, if the constructor is invoked with dot notation or square bracket notation, e.g. foo.bar() -thisrefers tofoo- just like every other JavaScript method -- see benefits).
Is there any practical reason why I would create a class or constructor over a factory function?
I've started JavaScript a few months ago and it's great how there are many different ways to create an object but, at times, all the different methods can be confusing. The more I research, the more it appears too that the other methods seem to be obsolete compared to Factory Functions.
Hi, from my limited understanding of both concepts there are two big differences between the two. Firstly, constructors create a prototype and factory functions don't. Secondly, factory functions return an object(which can include functions). so if we return functions that reference variables we don't want a user to directly access we effectively create a private variable(using closure). However I just discovered that constructors can have private variables by defining variables without the "this" keyword. So my question is as follows: Why would someone choose factory functions over constructors? I can't see a valid reason for wanting to avoid prototypal inheritance since it can provide extra behaviour when needed and constructors can implement private variables as well.
Since it returns an object its a factory function - it's already explained there.
Constuctor functions behaviour different from this, it doesn't return value:
function Person(name, age, location, occupation){
this.name = name
this.age = age
this.location = location
this.occupation = occupation
}
Person.prototype.printDetails = function(){
console.log(`My name is ${this.name} and I'm ${this.age} years old. I live in ${this.location} and I work as a ${this.occupation}.`);
};
const secondUser = new Person('Johnny', 25, 'London', 'Driver');
secondUser.printDetails();
I used definition of methods by extending prototype just to separate constructor function, and you can still define a methods inside constructor function as well:
function Person(name, age, location, occupation){
this.name = name
this.age = age
this.location = location
this.occupation = occupation
this.printDetails = function(){
console.log(`My name is ${this.name} and I'm ${this.age} years old. I live in ${this.location} and I work as a ${this.occupation}.`);
};
}
const secondUser = new Person('Johnny', 25, 'London', 'Driver');
secondUser.printDetails();
Javascript takes its roots in Prototypal Inheritance:
// Prototype
var ProtoCtr = function ProtoCtr(name, age) {
this.name = name;
this.age = age;
};
ProtoCtr.prototype.printDetails = function printDetails() { console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old`); };
var protoInstance = new ProtoCtr('John', 21);
protoInstance.printDetails();
In short:
Functions expose prototypes
Defines a
thiscontext referring to the instanceUse the
newkeyword to create an instance
Then, with ES6, the language offered the possibility to use the class keyword (to please classical OO developers). It's a syntax which allow to group the declaration of your object in a block, avoiding to extend the prototype "manually". Under the hood, it does the exact same thing than the prototypal model, it's just an other way to write it.
// Class
class ClassCtr {
constructor(name, age) {
this.name = name;
this.age = age;
}
printDetails() {
console.log(`Hi, I'm ${this.name} and I'm ${this.age} years old`);
}
}
var classInstance = new ClassCtr('John', 21);
classInstance.printDetails();
Key concepts are about the same, but you don't use prototype to expose methods to your instance but declare it directly inside the class block.
Then, there is the factory pattern, which is not a language specs but a pattern, a way to do things. It's my personal favorite. The factory function builds and return the instance. With this method, you get rid of the new keyword and don't need the this anymore to refer to you instance:
// Factory
var factoryCtr = function factoryCtr(name, age) {
var instance = {
name: name,
age: age,
printDetails: function printDetails() { console.log(`Hi, I'm ${instance.name} and I'm ${instance.age} years old`); }
};
return instance;
}
var factoryInstance = factoryCtr('John', 21);
factoryInstance.printDetails();
Even if Class is not covered by this talk, I recommand you to watch this video: https://www.youtube.com/watch?v=ya4UHuXNygM
I hope this helped :)