1

I've started learning JS and I'm trying to avoid using 'new' and 'this' and found my preferred method of using a factory function pattern. The downside is that instantiating new objects increases the overhead as each method is duplicated for each instance.

I then extracted each method into another object so I can use differential prototypical inheritance, i.e. each method only exists once and the calling function references this new object; this way I have both data persistence and prototypical inheritance extensibility.

Are there any pitfalls in this pattern?

var square = function(name) {
    var data = { 'x1': 0, 'x2': 10, 'y1': 0, 'y2':10 };
    return {
        draw : function(){ square.methods.draw(data); }
    };
};

square.methods = {
    draw : function(data) { doStuff }
};

//usage
var newSquare = square('xyz');
newSquare.draw();

Edit:

Thanks for all the prompt replies, I sincerely appreciate all the help. I've accepted Bergi's answer as it mirrors what I was trying to do; guess I have to suck it up and use 'this' ;)

Edit 2:

I was trying to avoid constructors after reading this (scroll down to 'Closures for events and callbacks' section); each object I'll be creating will be using callbacks and his factory function solution ticked my boxes of simplicity.

Data
  • 1,337
  • 11
  • 17
  • 5
    Why do you want to avoid using `new`, `this`, and `object.prototype`? – David Sherret May 28 '14 at 19:02
  • You're still duplicating a method with every new object. I don't see the difference. – cookie monster May 28 '14 at 19:06
  • @ dhsto - Because new and this mash my head. I want to keep my modules purely as objects and then namespace functionality by augmenting objects. – Data May 28 '14 at 19:06
  • You can't avoid the "ugly" parts of a language when coming into one. That just makes you more scared of it when you use it with more advanced techniques and see other examples of code that use the parts you're trying to avoid. Just read up on the "this" keyword and first class function. You'll start to see how, while sure, some parts of JS are different from your standard programming language, others are exactly the same. – Kodlee Yin May 28 '14 at 19:06
  • @cookie monster - isn't there only ever one instance of square.methods? – Data May 28 '14 at 19:08
  • 1
    @Data: Yes, but `draw : function(){ square.methods.draw(data); }` is created on every individual instance. No matter what, if each instance needs to have its own `data` that is protected in a variable scope, you'll need to have a duplicate method on each instance to access it. – cookie monster May 28 '14 at 19:16
  • ...is your avoidance of `new` and `this` motivated by Crockford's opinions? Just curious. – cookie monster May 28 '14 at 19:21
  • @cookie monster - Crockford's use of object.beget made me adamant to use object.Create() rather than constructor functions, so I played around trying to create my own constructor pattern as in OP, so I can keep data persistent whilst using only objects. I don't think 'new' and 'this' belong in JS as it's trying to appease the Java / classical OO crowd; JS !== Java / C++ et al. – Data May 28 '14 at 19:30
  • 1
    @Data: The words themselves were for the purpose of familiarity. Their use is entirely different because JS !== Java. To say the words don't belong in JS is odd. It's an entirely different language so there's nothing wrong with those words having their own use. Since you're a JS beginner, it's wise to not form such strong opinions so early, because to be blunt, you don't know enough at this point to make sufficiently informed decisions. Take everything Crockford says with a ***huge*** grain of salt. He talks like there's only one way, his way, when IMO much of his advice has been rather poor. – cookie monster May 28 '14 at 19:52
  • @ cookie monster - wise words, thanks. I think that's the problem with JS, too much subjectivity from the get-go; there's many ways to skin the same cat and from a noob point of view, I'd prefer a de facto pattern. – Data May 28 '14 at 20:23
  • @Data: You're welcome, and yes JS is very flexible and I totally understand the desire to have a pattern based on a smaller subset. Nothing wrong with that at all. What it usually comes down to is the "decided upon" way rather than any objectively "right way". overall it seems like you're taking a good, rational approach to the problem. Best of luck. – cookie monster May 28 '14 at 20:38
  • 1
    If you want to learn more about how to "correctly" use constructor functions and prototype the maybe the following answer will help.http://stackoverflow.com/questions/16063394/prototypical-inheritance-writing-up/16063711#16063711 as cookie pointed out; Crockford's opinion is just an opinion. To support it I've seen him implement constructor functions wrong (not using object.create) and claim that parent constructor can't be re used. He wants encapsulation (private) but then modifies object and function prototypes (objects he doesn't own) – HMR May 29 '14 at 02:44

2 Answers2

2

Are there any pitfalls in this pattern?

Well, you've already named it: "each method is duplicated for each instance".

I'm trying to avoid using 'new' and 'this'

There's no good reason to do so unless you don't understand how they work. Is JavaScript's "new" keyword considered harmful?

Just go with the default constructor pattern:

function Square(name) {
    this.data = { 'x1': 0, 'x2': 10, 'y1': 0, 'y2':10 };
}
Square.prototype.draw = function(){ /* doStuff with `this.data` */ };

//usage
var newSquare = new Square('xyz');
newSquare.draw();

If you want to use a factory pattern, you still can do so with prototypical inheritance:

function square(name) {
    var instance = Object.create(square.methods);
    instance.data = { 'x1': 0, 'x2': 10, 'y1': 0, 'y2':10 };
    return instance;
};

square.methods = {
    draw: function(){ /* doStuff with `this.data` */ };
};

//usage
var newSquare = square('xyz');
newSquare.draw();

However, this pattern makes inheritance rather complicated.

Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Beat me to it, but I would also like to point out that OP's current definition of square does not instantiate a new instance (or data instance) of square for each call to it. It will simply re-use the one and only definition of it and throw away the "name". – Kodlee Yin May 28 '14 at 19:13
  • @KodleeYin: Huh? Every call to `square()` does create a new `data` object, a new return object and a new `.draw` function on that. – Bergi May 28 '14 at 19:15
  • Oop, in a pinch I misread the definition. – Kodlee Yin May 29 '14 at 03:10
0

I wanted to add some additional pitfalls to what has already been said:

1. Makes maintenance of the code a little more difficult.

For example, if you rename the draw function you have to change it in three places instead of one.

Defining methods is also a little more work than usual.

2. Using instanceof

You can't use instanceof. For example, in your code above try doing this:

newSquare instanceof square

It returns false.

3. Extremely minor pitfall: Places two functions on the call stack with each function call

This is an extremely minor issue, but every time you are making a function call you are putting two functions on the call stack.

Additionally, you need to define functions on the object itself instead of the object's prototype. That's not as efficient, but it's such a small issue--especially since the main function bodies are being stored in a singleton elsewhere.

I actually wouldn't even consider this third point a pitfall, but I'm saying it anyways.

David Sherret
  • 101,669
  • 28
  • 188
  • 178