8

I'm reading this article about perils of trying to mimic OOP in JavaScript and there's the following:

In JavaScript, factory functions are simply constructor functions minus the new requirement, global pollution danger and awkward limitations (including that annoying initial capitalized letter convention).

JavaScript doesn’t need constructor functions because any function can return a new object. With dynamic object extension, object literals and Object.create(), we have everything we need — with none of the mess. And this behaves just like it does in any other function. Hurray!

Am I right to assume that given this approach we should replace this code:

function Rabbit() {
    this.speed = 3;
}

Rabbit.prototype = {
    this.getSpeed = function() {
        return this.speed;
    }
}

var rabbit = new Rabbit();

With this:

function RabbitFactory() {
    var rabbit = {
        speed: 3
    };

    Object.setPrototypeOf(rabbit, {
        getSpeed: function() {
            return this.speed;
        }
    })

    return rabbit;
}

var rabbit = RabbitFactory();
2
  • 1
    I think you are over-engineering this a bit. You've fallen to another trap of mimicing OOP. A factory function is just a function, what makes it a factory is that you can use new with it to get an instance. That's your first example. The second one is doing a similar thing to what would happen if you call bunny = new Rabbit() as you are replacing this with bunny = RabbitFactory() yet the latter is less flexible. What the article suggests is to use less code, so what you should do is something like bunny = Object.create(rabbit) Commented Sep 17, 2016 at 12:34
  • Example of factory functions () => ({}) // returns object () => [] // returns array Commented Aug 23, 2022 at 7:08

6 Answers 6

10

Basically I would distinguish 3 approaches to create an object in JS:

  • Class
  • Constructor
  • Factory

Here are 3 examples (considering your Rabbit's one)

// class
class Rabbit {
  constructor() {
    this.speed = 3; 
    // it would be so nice to have just 'static const speed = 3;' instead of
    // using constructor for that
  }
  getSpeed() {
    return this.speed;
  }
}
let rabbit1 = new Rabbit();

// constructor
function ConstructorRabbit(){ }
ConstructorRabbit.prototype.speed = 3;
ConstructorRabbit.prototype.getSpeed = function() {
  return this.speed;
};
let rabbit2 = new ConstructorRabbit();

// factory
const rabbitProto = {
  speed: 3,
  getSpeed() {
    return this.speed;
  }
};
function factoryRabbit () {
  return Object.create(rabbitProto);
}
let rabbit3 = factoryRabbit();

I'm not sure that there are so many pros to use only factory for creating objects, but probably I can single out the one. As mentioned in the article if we refer to very famous 'Design Patterns', so we should prefer object composition instead of class inheritance. And I'm totally agree with that postulate, thus returning back to JS and ES6 classes, we can say that prototype delegation may be better than class inheritance in some cases.

But also, we shouldn't forget this (as mentioned in the article as well) statement: "How it’s implemented doesn’t matter at all unless it’s implemented poorly". And this one, I would say, is a really good one.

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

Comments

7

Many answers here suggest Constructor Functions, although the name of the questions has to do with Factory Functions.

Factory Functions look like the following:

const RabbitFactory = () => {
  const speed = 3;
  const getSpeed = () => speed;
  return { getSpeed }
}
const rabbit = RabbitFactory();
rabbit.getSpeed() // -> 3
rabbit.speed // -> undefined!

I like Factory functions more than Constructor function because:

  • You don't need to get messy with prototyping
  • You don't need to use the Object constructor
  • Gives you the ability to choose what is private and what is public in your factory
    (in my example, speed is private and this is a good practice. If you want to read the value of rabbit's speed, use the GetSpeed getter)

3 Comments

Could you also add an example of a Factory function?
Ah, sorry! I guess I misunderstood "Constructor functions look like the following:" I am sorry, I was just reviewing the "late answer" review queue, I am not an expert on JS at all. Maybe you can reword your answer a bit, to avoid other people misunderstanding it as I did. Thanks!
@MaxVollmer, I am sorry because that was a mistake of mine.. meant factory function ><.. I fixed it. For further explanation of differences and pros and cons, you can read this stackoverflow thread
4

No, that is wrong. You should not use Object.setPrototypeOf, better use Object.create (though it makes no difference for the result). And if you create the prototype from an object literal every time, it loses all of its sharing advantages, so you should either drop that completely or move it outside the function to make it static. The correct™ way to write the factory function would be

const protoRabbit = {
    getSpeed: function() {
        return this.speed;
    }
};
function createRabbit() {
    var rabbit = Object.create(protoRabbit);
    rabbit.speed = 3;
    return rabbit;
}

var rabbit = createRabbit();

5 Comments

thanks, do you write your code in that style without constructor functions?
@Maximus Occasionally, when it fits :-) Usually I go for ES6 classes though. I've rarely found "awkward limitations" in constructors.
I see, thanks. I'm just getting into all this functional buzz around JavaScript and not sure whether I should focus on it. What do you think?
Functional programming is very powerful and I can only recommend to learn about it, but maybe Haskell is a better language to learn it. JavaScript is a bit limited in some regards, so you should first learn what is possible in general and then be able to decide how far you can sensibly apply it in js.
Yeah, maybe I'll get to Haskell some day. Thanks
3

I don't like these answers because they use the this keyword. You can avoid this altogether and still use a factory function like this:

function createRabbit() {
    var speed = 3;
    return {
        getSpeed: function() {
            return speed;
        }
    }
}

var rabbit = createRabbit();
console.log(rabbit.getSpeed());

This guy has a good article about it: http://radar.oreilly.com/2014/03/javascript-without-the-this.html

1 Comment

How would you implement prototype delegation with this pattern?
2

The simplest pattern is:

function RabbitFactory() {
  return {
    speed: 3,
    getSpeed() { return this.speed; }
  };
}

var rabbit = RabbitFactory();

Comments

0

This text awfully sounds like a Douglas Crockford speech. Anyway it mentions this pattern.

function RabbitFactory(){
rabbit = Object.create({ getSpeed: function() {
                                     return this.speed;
                                   }
                       });
rabbit.speed = 3;
return rabbit;
}

setPrototypeOf/getPrototypeOf or the __proto__ property are introduced in ES6 whereas Object.create() is an ES5 functionality. setPrototypeOf/getPrototypeOf or the __proto__ are good for subclassing thingies but you shouldn't use it together with Object.create()

I would keep using constructor functions though.

8 Comments

Don't create prototypes inside a function.
Don't even mention __proto__, it's deprecated and should be removed from our minds. Btw, getPrototypeOf is ES5
@Bergi The pattern you give is an exact copy of my snippet other than i am using an object literal whereas you are using a reference for the prototype. On the other hand __proto__ is not depreciated please don't mislead people. It's standardized in ES6 and a beautiful tool use when needed.
The location of the object literal matters, that's what I tried to point out :-)
__proto__ is deprecated, if you don't believe it read the spec yourself. It's been standardised for backwards-compatibility only, and should not be used anywhere any more. getPrototypeOf/setPrototypeOf are the proper tools that ES6 gives us.
|

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.