2

I was reading this page (the factories section in particular).

It mentions to avoid using the new keyword to prevent the case of accidently forgetting it. It suggests using factories.

Page's new Example:

function Bar() {
    var value = 1;
    return {
        method: function() {
            return value;
        }
    }
}
Bar.prototype = {
    foo: function() {}
};

new Bar(); 
Bar(); // These are the same.

Page's factory example:

function Foo() {
    var obj = {};
    obj.value = 'blub';

    var private = 2;
    obj.someMethod = function(value) {
        this.value = value;
    }

    obj.getPrivate = function() {
        return private;
    }
    return obj;
}

Factory Cons:

  • It uses more memory since the created objects do not share the methods on a prototype.
  • In order to inherit, the factory needs to copy all the methods from another object or put that object on the prototype of the new object.
  • Dropping the prototype chain just because of a left out new keyword is contrary to the spirit of the language.

The avoiding new to prevent issues in case you forget is understandable. But what I'm not quite grasping is that they say the factory example takes more memory since it's not using the prototype functions. So why not use something like this instead?

My Solution:

var Foo = function () {
    var foo = function () {

    };
    foo.prototype = {
        bar: function () { }
    };
    return new foo();
};

Question: Am I missing something that makes this not a better solution? Does my solution remove the listed cons of the factory method, why or why not?

Shelby115
  • 2,816
  • 3
  • 36
  • 52
  • 1
    Yes this creates a new constructor **every time** you call the factory method – soktinpk Jul 17 '14 at 21:09
  • I clarified my question a bit. I knew mine created a new object each time, I just was curious if it removed the factory-method cons that were listed by linked site (which I included in my question now). Or added any pros/cons for that matter. – Shelby115 Jul 17 '14 at 21:13
  • [Don't belief them!](http://stackoverflow.com/q/383402/1048572) As all these code snippets show, there are worse thing that can go wrong. [There are better ways](http://stackoverflow.com/a/24020621/1048572) to counter forgotten `new`s. – Bergi Jul 17 '14 at 22:00

1 Answers1

1

Alright, let's take the new example:

function Bar() {
    var value = 1;
    // Whoops, sorry
    // As Bergi points out, if you have a return value then that overrides the normal object that is returned by new
    // Didn't even notice you have a return in here!
    /*
    return {
        method: function() {
            return value;
        }
    }
    */
    // This does the same thing (approximately) but now calling "(new Bar()) instanceof Bar" will be true
    this.method = function() {
        return value;
    };
}
Bar.prototype = {
    foo: function() {}
};

var b = new Bar();

In the google chrome console, b has a property called __proto__. Basically, when you call b.foo(), first the browser looks for a method called foo. If it doesn't find it, it looks in b.__proto__ and b.__proto__.__proto__ and so on.

Notice: b.__proto__ === Bar.prototype

That means if you call new Bar() repeatedly, they will all have the same __proto__, which saves memory. (This has the side effect of, if you mutate Bar.prototype, it will change all instances of Bar's __proto__ as well).

Let's look at your factory method:

var Foo = function () {
    var foo = function () {

    };
    foo.prototype = {
        bar: function () { }
    };
    return new foo();
};

This doesn't save memory because it creates a new prototype, and a new constructor every time you call Foo(). So in other words, if

var f1 = new Foo(), f2 = new Foo();

The following return false: f1.constructor == f2.constructor and f1.__proto__ == f2.__proto__. What does this mean? It means that f1 and f2 don't share the same prototype so the objects have to be duplicated every time. Perhaps, you could do this:

var fooProto = {
  callFoo: function() { alert("test"); }
};
function Foo() {
    var foo = function() {};
    foo.prototype = fooProto;
    return new foo();
};

This would use the same amount of memory as the regular constructor.

Side edit: Modern browsers have a built-in function that does something like Foo in the last example. You could use Object.create(fooProto) (but only in newer browsers).

Also, note that __proto__ is technically a hidden, read-only property (although some browsers let you write to it). It was only used for the sake of showing what goes on behind the scenes, and it shouldn't be used in real code.

soktinpk
  • 3,778
  • 2
  • 22
  • 33
  • 1. Would moving the `foo` & `foo.prototype` outside of the `Foo` function solve that? 2. Thanks for explaining about `__proto__` was helpful. – Shelby115 Jul 17 '14 at 21:17
  • `b.__proto__ === Bar.prototype` - uh,**no**? The constructor `return`s an object, the prototype will be ignored. – Bergi Jul 17 '14 at 21:54
  • You just should *always* use `Object.create`, for clarity. And shim it in older browsers if necessary. – Bergi Jul 17 '14 at 22:01
  • @Bergi (comment 1) I'm talking about the first example, where you use `var b = new Bar();`. Then `b.__proto__ === Bar.prototype` – soktinpk Jul 18 '14 at 02:30
  • Yes, I do so as well. `function Bar(){ return {}; } Object.getPrototypeOf(new Bar) !== Bar.prototype`. Try it! – Bergi Jul 18 '14 at 02:43
  • 1
    @Shelby115: Yes, it would help. Actually it's not even so uncommon to have a `function makeFoo() { return new Foo(); }` that can - in contrast to `Foo` - be used without `new`. Notice that constructor names should always be capitalized. – Bergi Jul 18 '14 at 02:45