Update my JavaScript style! Avoiding global namespace pollution



  • In this job I'm doing a lot of JavaScript which I'm pretty rusty at.

    The old way of reducing global namespace pollution was to do something like this:

    if (typeof _myJS == 'undefined')
    {
        _myJS = new MyJS();
        _myJS.init();
    }
    
    function MyJS()
    {
        this.init = function ()
        {
            // register handlers, set up timeouts, etc whatever
            _myJS.utilityFunction(fart);
        }
    
        this.utilityFunction  = function()
        {
            // Maybe I get cookies, who knows
        }
    }
    

    This works but has a few disadvantages:

    • I'm not using "this" so the function-object refers to itself. I know some people will use var that = this; to establish a "this" that remains in the same scope. What I've usually done is use the function-object's global namespace name from within the function-object. This is obviously not ideal.
    • It still uses one variable of global namespace. I know I could wrap this in parens to make it a "self-closure" (not sure that's the right term?) but if I do so, my setTimeouts have nothing to refer back to when they timeout.

    It also has one big advantage:

    • It works perfectly with Intellisense on any IDE I've used, whereas I'm not sure how well alternative syntax would work with my IDEs.

    Would appreciate any comments from modern JS developers. Is this still a good/preferred way to solve the problem? If I put this in a code review, will the cool kidz on the point and laugh at me?



  • BTW if I remember right this is the method that Google Analytics' ga.js adopted, which I think is where I initially learned it. I'm not sure if GA is still written in this style.


  • :belt_onion:

    @blakeyrat that's similar to how I still do, though i try to avoid the singleton-like style of calling itself through its own name (what happens when i need to change the name due to namespace issues!?) I do the that = this generally, or cheekily named thats when I'm just goofing around.

    Though I usually declare it as

     MyJS = function() { /* init and property declarations is here */ }
     MyJS.prototype = { /*methods here*/ }
     _myJS = new MyJS();
    

    but i may be out of style too now.


  • Impossible Mission - B

    That's about as good as you can do. Even with languages that have a formal concept of a namespace as its own entity, the names of the namespaces you use for collecting other entities end up in the global namespace.


  • Impossible Mission - B

    @darkmatter said in Update my JavaScript style! Avoiding global namespace pollution:

    I do the that = this generally, or cheekily named thats when I'm just goofing around.

    I remember my CS professor in college joking that trying to explain the concept of this always reminded him of Bill Clinton talking about "what 'is' is."



  • Modern JS dev is almost always done with a build system, which would include either a node-style or ES6-style modules.

    So you would then just require() or import your code around and never ever touch the global namespace. There wouldn't be any global scope pollution, since each module would have its own isolated namespace. Everything would happen within this closed environment.

    Since it doesn't seem like you're doing modules, this seems like a reasonable alternative.

    var that = this within your class is a good idea. I usually name it thisClassName, to avoid confusion and help intellisense (eg. thisMyJS).

    Don't use prototype for service class setup, you can't have hidden variables with that.

    If I put this in a code review, will the cool kidz on the point and laugh at me?

    Yes. But that's unrelated to the code.



  • @cartman82 I have no idea what anything in your first two statements means. I'm debating if I should bother finding out.



  • @fwd said in Update my JavaScript style! Avoiding global namespace pollution:

    @cartman82 I have no idea what anything in your first two statements means. I'm debating if I should bother finding out.

    You would write something like this.

    import axios from 'axios';
    import {getCookie} from './utils';
    
    export class HTTPService {
      getUsers() {
        const cookie = getCookie();
        return axios.get(cookoe).then(/*...*/);
      }
    }
    

    You would put your code into http_service.js and utils.js. There would be other files, all importing from each other (something else would import HTTPService from 'http_service'). There would be one entry point file, that imports and sets up this entire dependency graph.

    Then you setup a build system (gulp or webpack) and point it at that main file. The build system will go through everything, and produce something like script.min.js, which is all bunched together, converted to "legacy" javascript and minified. You would then load that file from your html.



  • @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    Then you setup a build system (gulp or webpack) and point it at that main file. The build system will go through everything, and produce something like script.min.js, which is all bunched together, converted to "legacy" javascript and minified.

    Right; but how does script.min.js avoid namespace pollution? Or are you saying that gulp will put the entire script (plus dependencies) into a huge self-closure and wire it up to work with timeouts and event handlers?

    I totally understand what you're saying about the development workflow, what I don't get is how exactly that solves the problem I stated in the first post.



  • Actually I realized I can answer my own question, because I just built a TypeScript project using Gulp.

    It looks like this:

    (function (global, factory) {
        typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
            typeof define === 'function' && define.amd ? define(['exports'], factory) :
                (factory((global.libname = global.libname || {})));
    }(this, (function (exports) {
       /// code here
    })));
    

    So yeah, it does put it in a big ol' closure, but it's also doing something with a global.libname I frankly don't understand.



  • @blakeyrat said in Update my JavaScript style! Avoiding global namespace pollution:

    Right; but how does script.min.js avoid namespace pollution? Or are you saying that gulp will put the entire script (plus dependencies) into a huge self-closure and wire it up to work with timeouts and event handlers?

    Yes. Each module's code gets its own closure. Same goes for vendor libraries (as long as you use npm modules to install them). The only way modules communicate between themselves is through imports and exports. Never through global object.

    I am not sure why you think there is a problem with "timeouts and error handlers". You can still freely use the window object from within this compiled code. Call setInterval and window.document whenever you want, just don't attach anything to the global scope.



  • @blakeyrat said in Update my JavaScript style! Avoiding global namespace pollution:

    So yeah, it does put it in a big ol' closure, but it's also doing something with a global.libname I frankly don't understand.

    Looks like global is window objects. And it will attach something for some reason.

    Doesn't matter. Whatever it does, after a while you learn to trust the machine and live within this virtual world of ES6 and modules. The same way C++ programmers almost never touch the machine code if they can help it.



  • @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    I am not sure why you think there is a problem with "timeouts and error handlers".

    Because if the object's in a closure, and the setTimeout is in the window or document namespace, I would imagine they're not able to talk to each other, yes?

    I mean I'm obviously wrong, but I'm not wrapping my head around why.



  • @blakeyrat said in Update my JavaScript style! Avoiding global namespace pollution:

    Because if the object's in a closure, and the setTimeout is in the window.whatever namespace, I would imagine they're not able to talk to each other, yes?

    function closure() {
      var window = {
        setTimeout: function () {}
      };
    
      function myCode() {
        window.setTimeout();
      }
    }
    

    myCode can talk with window object from the outside closure.

    Now imagine that there is a little bit of magic wheres you can automagically access anything on window. So you can just call setTimeout().

    Why do you think this shouldn't work?



  • I'm not following.

    @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    myCode can talk with window object from the outside closure.

    Anybody can talk to window from anywhere in the DOM at any time. So... yes?

    @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    Now imagine that there is a little bit of magic wheres you can automagically access anything on window.

    Again, that's not magic. window is a big global namespace that contains everything, so everybody can access it.

    @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    So you can just call setTimeout().

    Of course you can call setTimeout() from inside the closure. My concern is once your setTimeout() code runs, how can it access anything inside the closure? The closure has no name and is not addressable, AFAIK.

    @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    Why do you think this shouldn't work?

    See above.

    In my crap example, I can have my setTimeout() code refer to _myJS and it can access whatever methods it needs there-- that's because _myJS specifically is named in the global window namespace.

    If my library were in a big ol' closure, I wouldn't be able to refer to it from anywhere outside the closure, because (to my understanding) it has no name. It's not in window. My setTimeout() handler code can't refer to _myJS because _myJS isn't anywhere at all.

    Like I said above, I'm obviously wrong here. Just trying to wrap my head around how I'm wrong.



  • @blakeyrat

    For example:

    myjs.js

    export function MyJS() {
      var thisMyJS = this;
    
      function action() {
        // your code
      }
    }
    

    main.js

    import {MyJS} from './myjs.js';
    
    var myJS = new MyJS();
    
    setInterval(function () {
      myJS.action();
    }, 1000);
    

    In this case, main.js would be something like a service container, which imports and initializes all other parts of your code.

    If you want to do it singleton style, you can also import/export variables.

    myjs.js

    function MyJS() {
      var thisMyJS = this;
    
      function action() {
        // your code
      }
    }
    
    export var myJS = new MyJS();
    

    main.js

    import {myJS} from './myjs.js';
    
    setInterval(function () {
      myJS.action();
    }, 1000);
    


  • Either nothing you just typed addresses my confusion at all, or you're so far above my head than I'm not processing it.

    (Or you're just saying that the import psuedo-keyword puts shit in the global scope, but in a circular way.)


  • Java Dev

    @blakeyrat said in Update my JavaScript style! Avoiding global namespace pollution:

    If my library were in a big ol' closure, I wouldn't be able to refer to it from anywhere outside the closure

    That's where the magic of callbacks comes in. Your handler is a function inside the closure. The window code cannot normally access that handler, but by passing it the handler as a callback it gains access to it. The handler itself is inside your closure, so it has access to everything that anything else in your closure also has access to.



  • @blakeyrat can you post an example of what you want done with setInterval?



  • @pleegwat said in Update my JavaScript style! Avoiding global namespace pollution:

    That's where the magic of callbacks comes in. Your handler is a function inside the closure.

    So what you're saying is that I have to define my setIntervals like:

    (closure){
      var that = this;
    
      this.setIntervalHand = function () {
        // do stuff in this function has access to that and can do shit with it
      }
    
     this.init()
     {
      setTimeout( this.setIntervalhand, 5000 );
     }
    });
    

    That works, and I get why. Because the setTimeout callback is keeping the closure "in scope" so the GC can't dispose of it. Fair enough.

    But this:

    (closure){
      var that = this;
    
      this.setIntervalHand = function () {
        // do stuff in this function has access to that and can do shit with it
      }
    
     this.init()
     {
      setTimeout( function() { that.setIntervalHand() }, 5000 );
     }
    });
    

    Does not work because by the time setTimeout fires its anonymous function "that" is meaningless to it.

    ... am I following?

    So it means I can still use setTimeout, but only if I'm creating the setTimeout handler function inside the closure and refer directly to it when I call setTimeout without using an anonymous function.

    JavaScript was confusing enough when it was goddamned straightforward. #GrumpyOldMan



  • @blakeyrat said in Update my JavaScript style! Avoiding global namespace pollution:

    Does not work because by the time setTimeout fires its anonymous function "that" is meaningless to it.

    Incorrect. The anonymous fn that you give to setTimeout will keep reference to that. setTimeout does not figure into it.

    Just follow what is defined inside which curly brackets, like a pyramid. The anon function you give to setTimeout is 2 levels above that, but still in the same closure chain.

    Chrome debugger has a neat closure inspector in the right sidebar. Try setting up an example like this and pause inside your setTimeout handler. It will show you the full stack of captured closures underneath it.



  • @cartman82 said in Update my JavaScript style! Avoiding global namespace pollution:

    Incorrect. The anonymous fn that you give to setTimeout will keep reference to that. setTimeout does not figure into it.

    Ok, so the anonymous function's scope will be window, but the reference to that will still do the correct thing.

    Hm, I guess now that I think about it, I don't know why I thought it wouldn't. Oh well.



  • @blakeyrat setTimeout can work with lambdas, then anything inside it should work.

    Maybe you're thinking in setTimeout with a string parameter? Then you would have the problem you describe.

    Edit: didn't see there was another page of comments



  • @wharrgarbl said in Update my JavaScript style! Avoiding global namespace pollution:

    Maybe you're thinking in setTimeout with a string parameter? Then you would have the problem you describe.

    Possibly, or it was due to some bug in like IE 5.5 that's been sticking in my head for the last decade. Who knows. I started doing JavaScript dev a long time ago, which ironically probably puts me at a disadvantage against people who learned all this shit last week.



  • @blakeyrat
    Never fear! In another week, you'll all be on even ground again! 🍹



  • @izzion Now just wait until I show you GWT and you start writing Java instead.



  • @blakeyrat said in Update my JavaScript style! Avoiding global namespace pollution:

    (function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (factory((global.libname = global.libname || {})));
    }(this, (function (exports) {
    /// code here
    })));

    If I include one of these AMD-compatible modules in my webpage's DOM, is there a way to simply call into it from raw JS on the page without having to set up the whole build pipeline? When I'm literally just calling .start() and nothing else?



  • Ok, I understand that you need to include require.JS on the page and give it a base directory to load scripts from, so after some dinking around on StackOverflow, I found a code example and created this:

    // Adapted from: https://stackoverflow.com/questions/12697238/load-requirejs-module-inline-the-html-body
    function configureRequireJS()
    {
        var scripts = document.getElementsByTagName("script");
        var src = scripts[scripts.length-1].src;
        var baseUrl = src.substring(src.indexOf(document.location.pathname), src.lastIndexOf('/'));
    
        require.config({
            baseUrl: baseUrl,
        });
    }
    
    configureRequireJS();
    

    So I believe that gives me a configured and working (hopefully) requireJS. Now to just figure out how to load a specific module using it...



  • Ah I already had the hard stuff done. All that was left was:

        var libConfig ={
        };
    
        require(['lib'], function (lib) {
            lib.start(libConfig);
        });
    

Log in to reply