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();
1

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Great way to explain what's going on. How can I get round it though?
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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.