0

Despite having read all the usual tutorials on the Javascript module pattern, there's still something about scoping which I clearly don't understand.

pushing to a module-wide list does what I'd expect it to do. Setting a module-wide list with = doesn't. What am I doing wrong here?

var MODULE = MODULE || {};

MODULE.Submodule = (function(){
    var foo = [],
        bar = [];

    var init = function() {
        foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
        bar.push('a'); // But this works as expected
        console.log('foo: ' + MODULE.Submodule.foo); // foo:
        console.log('bar: ' + MODULE.Submodule.bar); // bar: a
    }

    return {
        init: init,
        foo: foo,
        bar: bar
    }
}());

MODULE.Submodule.init();
apxcode
  • 7,696
  • 7
  • 30
  • 41
LondonRob
  • 73,083
  • 37
  • 144
  • 201
  • possible duplicate of [Javascript revealing module pattern, public properties](http://stackoverflow.com/questions/8893099/javascript-revealing-module-pattern-public-properties) – LondonRob May 02 '14 at 14:37

1 Answers1

3

This JSFiddle http://jsfiddle.net/SwBLk/1/ may help explain what's happening:

var MODULE = MODULE || {};

MODULE.Submodule = (function(){
var foo = [],
    bar = [];

var init = function() {
    console.log(MODULE.Submodule.foo === foo);
    foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
    console.log(MODULE.Submodule.foo === foo);
    bar.push('a'); // But this works as expected

    console.log('foo: ' + MODULE.Submodule.foo); // foo:
    console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}

return {
    init: init,
    foo: foo,
    bar: bar
}
}());

MODULE.Submodule.init();

The first boolean check return TRUE because both objects reference the same object. The second boolean check returns FALSE because you have replaced foo with a new object and the references don't point to the same underlying object anymore.

You are replacing the foo array with a reference to a new array when you re-assign a new array to it.

When you execute the IIFE, you assign a reference to a very specific version of "foo" on the return statement. That is the reference you then access when you call MODULE.Submodule.foo. When you go in and replace foo = [ "a", ... ], you are replacing the object foo, but not replacing the reference to it in the MODULE.Submodule object.

EDIT: How do you get around this issue?

1) Ideally, you don't replace the entire array, but only clear & re-initialize it on init() call:

MODULE.Submodule = (function(){
var foo = [],
    bar = [];

var init = function() {
    // Clear our the array if it already contains data (in case your calling "init" more than once)
    // This method is documented to be the fastest way to clear out an array, referenced here:
    // http://stackoverflow.com/questions/1232040/how-to-empty-an-array-in-javascript
    while(foo.length > 0) {
        foo.pop();
    }

    foo.push('a');
    foo.push('b');
    foo.push('c');

    bar.push('a'); // But this works as expected

    console.log('foo: ' + MODULE.Submodule.foo); // foo:
    console.log('bar: ' + MODULE.Submodule.bar); // bar: a
}

2) Your second option is to use what I'd call "dynamic getters" (working fiddle: http://jsfiddle.net/6zVcP/):

var MODULE = MODULE || {};

MODULE.Submodule = (function(){
var foo = [],
    bar = [];

var init = function() {
    foo = ['a','b','c']; // This doesn't set MODULE.Submodule.foo
    bar.push('a'); // But this works as expected

    console.log('foo: ' + MODULE.Submodule.foo()); // foo:
    console.log('bar: ' + MODULE.Submodule.bar()); // bar: a
}

var modify = function()
{
    foo = [];
    foo.push("test");
}

return {
    init: init,
    modifyArrays: modify,
    foo: function() { return foo; },
    bar: function() { return bar; }
}
}());

MODULE.Submodule.init();
MODULE.Submodule.modifyArrays();
console.log(MODULE.Submodule.foo());

This allows you to do whatever you want to the foo object while the getter will always return the latest reference.

Bernardo
  • 1,030
  • 1
  • 12
  • 22
  • Great way to explain what's going on. How can I get round it though? – LondonRob Apr 24 '14 at 09:52
  • Well, ideally you wouldn't replace the array, you would simply empty & initialize the same array object. If that's not an option, then you'd need a dynamic getter. Editing answer to explain. – Bernardo Apr 25 '14 at 14:37