How to be a good citizen in JavaScript

Discussion in 'Mod Discussions' started by ORFJackal, January 12, 2014.

  1. ORFJackal

    ORFJackal Active Member

    Messages:
    287
    Likes Received:
    248
    Quite many of the PA mods do not keep their local variables local, but they are polluting the global namespace. When you input window into the debugger console when a game is running, you will see tens of variables that leak the internal state of some mods ($leftPanel, $middlePanel, $rightPanel, EnergyKilo, Energy_Ratio, Metal_Net, Metal_Ratio, ReportData, ReportPlayer, ReportTeam, ReportedPlanet, RunningGameData, StatsReportData, ValueChangeAccumulator, actionsSinceLastTick, addDeatchListener, addedDeathListener, allow_console, apmCnt, apply_keybinds, apply_keybindsHotbuild, callServerTime, checkPaStatsVersion, cleanupLegacyStorage, clientInformation, closed, commanderImages... the list goes on and on) and some of them are so common words that a naming conflict with some other mod is just a matter of time (b, c, length, i, name, screenX, screenY, status etc.) which may cause weird bugs when those conflicting mods are used together.

    In JavaScript the only way of scoping variables is the function scope. The way to isolate your variables is to use the JavaScript Module Pattern: put the variables and functions inside an anonymous function which you invoke right away. If there are some variables or functions that you want to expose for others to call, then put them into an object, return that object from the method and assign it to a global variable - the only global variable that your program creates - which will work as a namespace for your variables.

    For example like this:
    Code:
    var pamentor = (function () {
    
        // no outsider will see these variables and functions
        function cannotCallThis() {}
        var cannotTouchThis = 0;
    
        // all publicly exposed variables and functions need to be made members of this object
        var pamentor = {};
        pamentor.stats = ko.observable({});
    
        return pamentor;
    })();
    For operations that manipulate the DOM, you can pass the function to jQuery's $ like this:
    Code:
    $(function () {
    
        function loadTemplate(element, url) {
            element.load(url, function () {
                ko.applyBindings(model, element.get(0));
            });
        }
    
        // making that abovementioned object visible to Knockout
        model.pamentor = pamentor;
    
        // some more bootstrap code
        setInterval(pamentor.updateClock, 1000);
        setInterval(pamentor.updateStats, 5000);
    
        // manipulating the DOM, must be done after the document is ready
        createFloatingFrame('pa_mentor_frame', 100, 100, {'offset': 'leftCenter', 'left': 0});
        loadTemplate($('#pa_mentor_frame_content'), 'coui://ui/mods/PAMentor/live_game/pa_mentor.html');
    });
    This way your mod will not accidentally conflict with the variables from other mods, and others can access only those variables and functions that you explicitly expose. For example the pamentor.stats variable in the first code example can be accessed by others, but the loadTemplate function in the second cannot be accessed by anyone else.
    Last edited: January 20, 2014
  2. cola_colin

    cola_colin Moderator Alumni

    Messages:
    12,074
    Likes Received:
    16,221
    After chargrove recommanded it I just got my copy of Javascript: The good parts.
    A good book that opened my eyes to this issue as well.

    EDIT:
    Why use jqueries $(...) though? The mod code is run by the ui mod system of PA that runs within some on document load callback already, so I dont think it matters.
  3. DeathByDenim

    DeathByDenim Post Master General

    Messages:
    4,327
    Likes Received:
    2,125
    Oh, that's pretty neat. I just prepended everything I declared with "dNotes_" for my Notes mod to avoid conflicts. I was wondering why cola_colin had done stuff like that with his PA stats mod when I was trying to make my mod compatible with his.
  4. LavaSnake

    LavaSnake Post Master General

    Messages:
    1,620
    Likes Received:
    691
    Look's like I've been a bad JS citizen; updates coming soon to fix that!
    someonewhoisnobody likes this.
  5. maxpowerz

    maxpowerz Post Master General

    Messages:
    2,208
    Likes Received:
    885
    +1
  6. Raevn

    Raevn Moderator Alumni

    Messages:
    4,226
    Likes Received:
    4,324
    I think we're all a little guilty :oops:.
    Added to my "to-do" list.
    cwarner7264 likes this.
  7. ORFJackal

    ORFJackal Active Member

    Messages:
    287
    Likes Received:
    248
    maxpowerz and cola_colin like this.
  8. maxpowerz

    maxpowerz Post Master General

    Messages:
    2,208
    Likes Received:
    885
    shootall likes this.
  9. proeleert

    proeleert Post Master General

    Messages:
    1,681
    Likes Received:
    1,656
    Ditto
  10. elitedanzel

    elitedanzel Active Member

    Messages:
    151
    Likes Received:
    137
    I've been bad also

    Edit: dTimer, dReminderTimer, rCommanderHP updated.
    Last edited: January 13, 2014
  11. proeleert

    proeleert Post Master General

    Messages:
    1,681
    Likes Received:
    1,656
  12. cola_colin

    cola_colin Moderator Alumni

    Messages:
    12,074
    Likes Received:
    16,221
    When I saw those I thought "who would use such names in a global namespace"
    Turns out I did. Those are temp variables that are used as a part of the PA Stats loading process. I assumed Javascript has a block scope. Turns out it has not. xD If somebody overwrites those values PA Stats won't suffer though.
    Last edited: January 13, 2014
    LavaSnake and cwarner7264 like this.
  13. lokiCML

    lokiCML Post Master General

    Messages:
    1,973
    Likes Received:
    953
  14. proeleert

    proeleert Post Master General

    Messages:
    1,681
    Likes Received:
    1,656
    I use jshint.com don't know if there is much difference with jslint.
    I use some copied and modified code from uber and jshint doesn't like it bye the way :p

    But great tip anyhow.
  15. proeleert

    proeleert Post Master General

    Messages:
    1,681
    Likes Received:
    1,656
    orfjackal what ide do you use ?
  16. stormingkiwi

    stormingkiwi Post Master General

    Messages:
    3,266
    Likes Received:
    1,355
    Oh for crying out loud. Guys I *barely* scraped a pass in programming engineering year 1, and the very first thing we were told was to avoid global variables like the plague!

    (May have been one of the/the main contributing factor why I barely scraped a pass)
  17. ORFJackal

    ORFJackal Active Member

    Messages:
    287
    Likes Received:
    248
    IntelliJ IDEA
  18. cola_colin

    cola_colin Moderator Alumni

    Messages:
    12,074
    Likes Received:
    16,221
    The issue is that javascript makes it actually hard to prevent them. It is based on them and the missing block scope while having the typical scope syntax makes it even less obvious. Without first reading up on a good book it is very easy to go "ah I know this syntax and it seems to work" until you end up with a bunch of surprises.
  19. LavaSnake

    LavaSnake Post Master General

    Messages:
    1,620
    Likes Received:
    691
    Just updated my mods. Profile Pic Fixer and Custom In Game Timers are now good JS citizens. :)
  20. lokiCML

    lokiCML Post Master General

    Messages:
    1,973
    Likes Received:
    953
    jslint is developed by Douglas Crockford the author of JavaScript: the good parts. It's rather brutal in its quality checking but it shows all global variables.

Share This Page