Search code examples
javascriptmodule-patternrevealing-module-pattern

Javascript module pattern - Object.create


A bit of confusion regarding what exactly is going on here.

given this simple code :

stuff = (function(){
    function extendFoo(bar)
    {
        $.extend(this.foo,bar);
    }
    this.foo = {};

    return { foo : foo, extendFoo: extendFoo};
})();

Following this simple operation :

zz = Object.create(stuff);
zz.extendFoo({bar:'bar'});
vv = Object.create(stuff); //foo is still extended(??)

So from what I realized, operations conducted on the returned object form Object.create still affect the prototype of that Object, So when you create a new object, his prototype is changed, thus resulting in a 'modified' version.

This seems wrong on a lot of levels, Can anyone explain what's going on here?

this behavior is not reproducible using the following pattern :

stuff = (function(){

    function setFoo(bar)
    {
        this.foo = bar;
    }
    var foo;

    return { foo : foo, setFoo: setFoo};

})();

So I suspect $.extend is to blame here.

Any input would be great!


Solution

  • This problem has nothing to do with the module pattern and everything to do with prototypes.

    zz = Object.create(stuff) 
    

    creates a new object with stuff as its prototype.

    vv = Object.create(stuff) 
    

    creates a new object with the same stuff object as its prototype.

    Both zz and vv share the same prototype object, so if you modify the prototype object stuff, then changes will reflect in both derived objects zz and vv. It doesn't matter whether you're modifying the prototype via $.extend or other means, and it doesn't matter whether you're modifying the prototype via your derived object or your prototype.

    In your code, the foo object is attached to the prototype, not to the derived objects zz and vv. When extendFoo is invoked on the derived object zz, it is modifying foo on the prototype, so changes to foo are shared by the derived objects.

    In your second example, in setFoo

    this.foo = bar;
    

    what's happening is that you're setting the foo property on the derived object so now it overrides the prototype's foo. After you run setFoo on a derived object, that object's foo is no longer the prototype's foo. You can see this by running

    zz = Object.create(stuff);
    console.log(zz.hasOwnProperty('foo')); // false
    zz.setFoo(bar);
    console.log(zz.hasOwnProperty('foo')); // true
    delete zz.foo;
    console.print(zz.foo); // foo is the original foo, not null
    console.log(zz.hasOwnProperty('foo')); // false
    

    you'd actually get back your original foo instead of null.

    The reason why setFoo works in the second case is because you are no longer modifying the prototype, so the changes are no longer shared between derived objects. With the original's

    $.extend(this.foo,bar);
    

    you're modifying the object this.foo in place and not overriding. You can also see this via hasOwnProperty

    zz = Object.create(stuff);
    console.log(zz.hasOwnProperty('foo')); // false
    zz.extendFoo({bar:'bar'});
    console.log(zz.hasOwnProperty('foo')); // false