views:

56

answers:

3

The classical programmer in me is in love with the public/private paradigm of OO and I am finding it very hard to give up. This has caused me to run into an issue I was hoping you can help me with.

I have a singleton object called map. There can be only one. This map contains objects called Star (it's a game). A Star looks like this:

var Star = function(id, x, y) {
    this.id_ = parseInt(id);
    this.x_ = parseInt(x);
    this.y_ = parseInt(y);
};

In reality there a lot more arguments and properties. This is just an example.

Map is defined like this:

var map = (function() {
    // public interface
    return {
        init: function() {
            getStars();         
        },
        stars: {} // works but publicly accessible
    };

    var stars = new Array(); // fail

    function getStars() {
        $.getJSON("ps.php?jsoncallback=?", addStars);
    }

    function addStars(data) {
        for (var x=0, xx=data.stars.length; x<xx; x++) {
            map.stars[data.stars[x].id] = new Star(
                data.stars[x].id,
                data.stars[x].xpos, 
                data.stars[x].ypos
            );
        }
    }
})();

I think due to scope of addStars being a callback function of $.getJSON that it can't add the newly created star to the private variable of stars (FireBug says it is undefined and jQuery calls into abort after it tries to add it). However if I make stars a publicly visible object then it does work, but then stars is visible from the interface which is not what I want.

How can I keep stars private and have it accessible from the JSON callback?

+2  A: 

You can reference stars without the map in front of it.

var map = (function() { 

    var stars = new Array(); 

    function getStars() { 
        $.getJSON("ps.php?jsoncallback=?", addStars); 
    } 

    function addStars(data) { 
        for (var x=0, xx=data.stars.length; x<xx; x++) { 
            stars[data.stars[x].id] = new Star( 
                data.stars[x].id, 
                data.stars[x].xpos,  
                data.stars[x].ypos 
            ); 
        } 
    } 

    // this should be at the bottom.

    // public interface 
    return { 
        init: function() { 
            getStars();          
        }
    }; 

})(); 

defining addStars in the same scope context as stars creates a closure, making stars available whenever the function is called.

lincolnk
I am pretty sure I tried this but inspecting the planets still says undefined in Firebug. Also if I use Array then stars gives me a length of 36301 (the sum of all fields in all objects) whereas the use of Object allows me to work with each Star individually.
graham.reeds
A: 

Although stars could be considered a private member, it only actually exists within the scope of the self-calling function that defines map. You can't therefore access it via map.stars, however you can access as simply stars within the callback function.

var map = (function() {

    var stars = [];

    function getStars() {
        $.getJSON("ps.php?jsoncallback=?", function(data) {
            for (var x=0, xx=data.stars.length; x<xx; x++) {
                stars[data.stars[x].id] = new Star(
                    data.stars[x].id,
                    data.stars[x].xpos, 
                    data.stars[x].ypos
                );
            }
        });
    }

    // public interface
    return {
        init: function() {
            getStars();         
        }
    };
})();
roryf
+2  A: 

You defined stars after return. It never gets created, sure it will be undefined.

[See is in action]

var map = (function() {     

    var stars = new Array(); // define before return!

    function getStars() {
        $.getJSON("ps.php?jsoncallback=?", addStars);
    }

    function addStars(data) {
        for (var x=0, xx=data.stars.length; x<xx; x++) {
            stars[data.stars[x].id] = new Star( // use private stars
                data.stars[x].id,
                data.stars[x].xpos, 
                data.stars[x].ypos
            );
        }
    }

    // public interface
    return {
        init: function() {
            getStars();         
        }
    };
})();​
galambalazs