cinnaroll45
cinnaroll45

Reputation: 2790

Clarification need in class vs constructor function vs factory function

I'm playing around with ES6 classes and my eventual goal is to understand the difference between classes vs constructor functions vs factory functions. My current understanding is that constructor functions and classes are basically using the same design pattern, classes just being the new syntax for the constructor functions. Based on this assumption, I'm trying to create some examples that display the contrast between class/constructor functions and factory functions.

With the examples below I aim to return new objects and some methods that are inherited.

My first example is rather straightforward with the class syntax.

Example #1: Classes

class Person {
    constructor(name, age, location, occupation) {
        this.name = name;
        this.age = age;
        this.location = location;
        this.occupation = occupation;
    }
    printDescription() {
        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 firstUser = new Person('Tom', 30, 'Sydney', 'Teacher');
firstUser.printDescription();

With the second example, I'm trying to replicate the first one with a different approach but I'm not sure if this is factory constructor or a factory function.

Example #2: ?

function PersonMaker (name, age, location, occupation) {

    let person = {name, age, location, occupation};

    person.printDetails = () => {
        console.log(`My name is ${name} and I'm ${age} years old. I live in ${location} and I work as a ${occupation}.`);
    };

    return person;
}

const secondUser = PersonMaker('Johnny', 25, 'London', 'Driver');
secondUser.printDetails();

I need to clarify the pattern used in the second example and if it's not a factory function, how I could turn it into one?

Upvotes: 11

Views: 12261

Answers (3)

Polisas
Polisas

Reputation: 541

To avoid passing many arguments, you can do it like this.

const PersonMaker = description => {
  const {name, age, location, occupation} = description;
  return {
    printDetails: () => {
      console.log(
        `My name is ${name} and I'm ${age} years old. I live in ${location} and I work as a ${occupation}.`,
      );
    },
  };
};

const secondUser = PersonMaker({
  name: 'Johnny',
  age: '25',
  location: 'London',
  occupation: 'Driver',
});


console.log(secondUser.printDetails());

Upvotes: 4

Booster2ooo
Booster2ooo

Reputation: 1437

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 this context referring to the instance

  • Use the new keyword 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 :)

Upvotes: 4

Sergei Klykov
Sergei Klykov

Reputation: 1037

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();

Upvotes: 7

Related Questions