Shelby115
Shelby115

Reputation: 2867

JavaScript avoiding new keyword

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:

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?

Upvotes: 2

Views: 1449

Answers (1)

soktinpk
soktinpk

Reputation: 3888

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.

Upvotes: 1

Related Questions