6

Yes, this is another topic about global variables. I've searched quite a bit about them. But most of the topics is just about WHY not to use global variables and I'm convinced that I shouldn't, and I am more wondering HOW not to use them, and that I'm still unsure about.

I am working on a project, and it works wonders, but I am using about 50 global variables at the moment and that number keeps rising. Right now I've split things up in multiple .js files. Like load_chunks.js, load_images.js, render_map.js, player_actions.js, input.js, to spread the functions based on what they are for. And I've put all global variables inside settings.js.

Currently I use these global variables for these reasons:
1. Calculation based on some other global variables that doesn't change much or at all after loading. By doing this once, and store it in a global variable, I no longer have to repeat the calculation ever again. If I didn't store it in a (global) variable, Javascript would have to do the calculation many times each second, in some cases even up to few thousand times per second.
2. When the global variable is needed in many functions. Like there's this World-variable, that I use to hold data for the appearance of the world. This variable is a multidimensional array. World[y][x] for example. Load_chunks.js adds more data to this variable, or remove data if you move too far. This variable is also needed in render_map.js, to create the map, and it's also needed in player_action.js, to see if you can step on that particular location.
3. Settings; So a number in a variable that remains the same unless I change them in my script. Instead of going through my scripts, and change the numbers manually after a long search and thinking what the number was, I've put that number in 1 variable and call that variable numerous times in my scripts. These variables are in some cases also needed elsewhere.

Also I like to mention that I don't use classes, and perhaps for that reason I never got around using global variables...?

So how do I get rid of my global variables, or shouldn't I? I hope you can show me or write for me a script example (or a link to it) of how I should do it. That's the fastest way I learn.

Verkade89
  • 373
  • 2
  • 12
  • 1
    global variables are a sign your architecture is perhaps too tightly coupled thus preventing modularity and easy reuse of disparate functions ... you may want to refactor so there is no/less need for shared variables ... also if appropriate leverage natural variable scoping and put these variables inside a function as a closure where they are visible to children functions – Scott Stensland Apr 19 '15 at 16:12
  • possible duplicate of [How do I declare a namespace in JavaScript?](http://stackoverflow.com/questions/881515/how-do-i-declare-a-namespace-in-javascript) – Krzysztof Safjanowski Apr 19 '15 at 16:52
  • @vidit please [reconsider](http://stackoverflow.com/review/suggested-edits/7748012) adding the architecture tag. your rejection reason is my case in point, _Tags should help to describe what the question is **about**, not just what it contains._ – miraculixx Apr 19 '15 at 18:45
  • Architecture tag added, not sure if I should delete the others. Uhm, when I have to post some code, where am I going to do that? A new Answer, or edit the main post? – Verkade89 Apr 19 '15 at 19:45

3 Answers3

5

Put Variables Inside a Function Closure

One common way to eliminate a global is to put is inside a function closure:

(function() {
     var declareYourFormerGlobalHere = 0;

     // put all code that uses the variable inside this closure
     // the variable persists for this code to use, but is not actually global
})();

Here's are some usage examples:

// example keeping track of a running count and a cached DOM element
(function() {
    var cntr = 0, obj = document.getElementById("test");
    setInterval(function() {
        ++cntr;
        if (obj.value < cntr) {
            // do something
        } else {
            // do something else
        }
    }, 1000);
})();

// example keeping track of a time of last click
(function() {
    var timeOfLastClick = 0;
    document.getElementById("run").addEventListener("click", function(e) {
        var now = new Date().getTime();
        // only process click if more than 2 seconds have passed since last click
        if (now - timeOfLastClick > 2000) {
            // OK to process the click
        }
        timeOfLastClick = now;
    });
})();

Sometimes, you can actually enclose all your code or nearly all your code in a single closure like this and all your current globals become local variables inside the closure rather than actual globals. jQuery uses this technique to declare lots of persistent variables it uses, none of which are actual globally scoped.


Use a Single Namespace Object

Another common method of reducing the number of globals is to use the namespace concept. In this concept, you declare a single global object and put other persistent variables as properties of this single global object. This still leaves you with a single global variable, you can have as many properties as you want off this single global.

var myNamepaceObject = {};
myNamespaceObject.var1 = "foo";    
myNamespaceObject.var2 = "whatever";

This technique is also used by jQuery as all globally accessible functions that jQuery offers are available off the jQuery object such as jQuery.extend() or jQuery.contains(). jQuery exposes a single global variable and many other globally accessible functions are then available as properties of that single global object.


Module Pattern

What is commonly known as the "module pattern" uses a combination of these above two techniques, where you have a single module variable that both uses properties and closure variables.

var MODULE = (function () {
    var my = {},
        privateVariable = 1;

    function privateMethod() {
        // ...
    }

    my.moduleProperty = 1;
    my.moduleMethod = function () {
        // ...
    };

    return my;
}());

You can see a discussion of this design pattern in these references:

JavaScript Module Pattern: In-Depth

Learning Javascript - The Module Pattern

Mastering the Module Pattern

JavaScript module pattern with example

Community
  • 1
  • 1
jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • Added several more examples and references. – jfriend00 Apr 19 '15 at 16:55
  • See my reply on mohamed-stark post to see my answer about function closures and namespace objects. About the module pattern, I was thinking about something like that, I just had no idea how to do it. It looks a lot like working with classes, which I am pretty new with. Just not sure how it is performance wise, which I am worrying about (like always), but I'll test that out at jsperf.com. Also have to check if I will run in any serious problems by doing it. In some cases I need to place / run variables in the right order. – Verkade89 Apr 19 '15 at 17:09
  • @Verkade89 - You will not need to worry about performance issues with any of these patterns. Variables in closures actually perform faster than global variables (in the interpreter lookup path, they are found sooner than globals). Your comment to mohamed-stark does not really describe what you're doing well enough for me to see why you could not use the closure solution. If you want concrete advice about your specific code, then you will have to include actual code examples of globals you are trying to remove and we could be very specific and offer you code you could use directly. – jfriend00 Apr 19 '15 at 17:13
  • @Verkade89 - you should not be afraid of using objects in Javascript. It's highly worth learning and using and only when you're doing that are you really starting to use the true power of Javascript. It seems likely (from your description) that your code is also very procedural and converting to an object-based design often also lets you place state variables into object instance data rather than in globals. But, how to do that is very specific to the code you're writing so detailed suggestions would need to see your actual code. – jfriend00 Apr 19 '15 at 17:15
  • After looking again at the closure solution, it is indeed possible I think. I would have to test it out if I can make it work. I would need to remove events from body-tag. Because those events will trigger certain function directly, and those functions cannot find the variables inside the closure function. So have to use addEventListener, which I partly have already for touch-screens. Anyway that's what I think, I may be wrong. I am going to do some testing soon to get a better view and understanding how it works. – Verkade89 Apr 19 '15 at 18:14
  • @Verkade89 - Yes, switching to `addEventListener()` will allow your event handlers to be inside the closure which is generally considered better than code references directly in your HTML tags anyway. See [Unobtrusive Javascript](http://en.wikipedia.org/wiki/Unobtrusive_JavaScript) for a description of this general concept and my second closure code example which has an event listener inside the closure. – jfriend00 Apr 19 '15 at 18:17
  • @Verkade89 strongly suggest to adopt a view/model framework, such as backbonejs to get rid of global event handlers. they come with just about as many downsides as global variables. see my [answer](http://stackoverflow.com/a/29732924/890242) for details – miraculixx Apr 19 '15 at 18:21
  • @jfriend00 I've just updated my project with your solution to work with modules. It works pretty good. No more global variables, for exception of the module variables. I can't find many downsides other than some variables got a bit longer, as I need to type the module name and then the public variable inside it. Performance wise didn't improve, but it also didn't make things worse. It kept at the same level. So that's a relief for me. Thanks for all your help. – Verkade89 Apr 20 '15 at 17:51
5

I'm convinced that I shouldn't, and I am more wondering HOW not to use them, and that I'm still unsure about.

load_chunks.js, load_images.js, render_map.js, player_actions.js, input.js, strongly hint to a procedural implementation, that is your architecture probably has several functional pieces and you pass data around between these functions. That's where your global variables are coming from.

So how do I get rid of my global variables (...)

To change that, you need to structure your system in an object or component based approach, that is:

  • encapsulate data + respective functions by objects in your problem domain, e.g. have a World object that contains Avatar, Buildings, Airborne objects etc. (or whatever your project is about).

  • separate the problem domain's logic from view logic (e.g. use a MVC architecture)

  • then your project organises the interactions between view and model objects by essentially exchanging messages between them.

To facilitate this, I suggest these fine frameworks, or some equivalents suitable for your run-time environment:

  • requirejs - to encapsulate modules into globally managed components
  • backbonejs - to have an efficient, proven model/view, class/object model (actually built for use with a REST-style backend, but that's not a strict requirement)

Code structure

Typically, I structure my applications such that there is one .js file per each object component/module. A module in this sense contains both the class definition and the corresponding collection. The modules are managed by requirejs and the class definitions are done using backbonejs.

I hope you can show me or write for me a script example

/* define a class e.g. in someclass.js */
define(['backbone'], function($B) {
    var SomeClass = $B.Model.extend({
       // all instance variables, methods etc. follow
       someVar : value,
       someMethod : function() { ... }, 
    });
    return SomeClass;
});

/* use the class, e.g. in app.js */
require(['someclass'], function(SomeClass) {
   var instance = new SomeClass({ initial model attributes });
   var value = instance.get('attribute');
   instance.someMethod();
   ...
});
miraculixx
  • 10,034
  • 2
  • 41
  • 60
  • Yes, I do use some of those global variables to pass data around from 1 function to another. For example World[y][x], as I said in the main post. Also Player_New_Y, Player_New_X (I might eventually make that an object). Those change quite a lot and needed in many functions, from moving, to loading chunks, to rendering the map. There are some others like Tiles and Tilesets, load_images.js writes information just once to them, after that they are needed by render_map.js only. Most other global variables that I am using, are for a certain file only. – Verkade89 Apr 19 '15 at 19:08
  • When you say "structure your system in a object / component based approach", do you mean like classes are in C#? If that's the case then I have a pretty good idea how to do it. I can also make a lot of the global variables that I have now, being a variable that's only accessible (and only needed) in that class. So that would probably drop about 80% of the global variables. And if I do need certain variables (like World) in multiple functions, I simple return it as a object, just like the functions inside the class. Or am I wrong there? – Verkade89 Apr 19 '15 at 19:20
  • @Verkade89 _do you mean like classes are in C#_ - yes, and as in many other languages (if you know how to do it in C#, look at e.g. requirejs and backbonejs -- you will find similar concepts). From the other parts of your comments I think you have understood the idea, you just need to apply this to your code in Javascript. – miraculixx Apr 19 '15 at 20:44
  • Yes, I just tested it a bit. It works wonderful. I also don't believe that I need to put in a lot of work to change my code in this way. Well I need to change the variables names, so let say from World to Load_Chunks.World or whatever I call the class that then contains the World variable. Are there particular preferences where to put a variable. I mean I can put World in the load_chunks class but also in render_map class. load_chunks writes a lot to World, where as render_map reads a lot out of world. render_map makes the most use of world, few thousand times a second. Or doesn't it matter? – Verkade89 Apr 19 '15 at 21:09
1

i had the same problem before but it was a big problem as we had a third party javascript code and our code live in clients websites so we should find a way to omit the appearance of global variable so to be short: Use 1 global variable namespace and make all other to be properities for it:

GlobalVariable = {}
GlobalVariable.var1 = "var1"
GlobalVariable.var2 = "var2"

You can also make it more useful and divide it into groups:

 GlobalVariable.helper = {}
 GlobalVariable.helper.var1 = "var1"
 GlobalVariable.helper.var2 = "var2"

so you can use same names under different groups.

and if you want to Omit it totally you can use:

(function(document, window) {
    // all variables here is global only inside the scope
}(document, window));
mohamed-ibrahim
  • 10,837
  • 4
  • 39
  • 51
  • The first one is understandable for me and I also thought about doing something like that. Just 1 variable, then make it a object. Categorizing by adding more objects in it. I just wasn't sure if that was the proper way. The one with (function(){ }()) will not work I think, or at least it will be devious. As that's going to be my main function which will have to load the 'setting' variables, that I have globally now, every single time, and in there I have to call the functions that will handle everything to render the map, I believe. – Verkade89 Apr 19 '15 at 16:37
  • Yes, the scope will limit you and in case you use global variables cross multiple files will be hard to handle, but for using name space it solve the mess problem for having lots of variables and as i said for third party app that was acceptable from clients to have 1 global variable in their website but to be unique for youe app. – mohamed-ibrahim Apr 19 '15 at 17:04
  • that's basically just moving global variables from the document/window scope into the GlobalVariable object. However, it doesn't get rid of the problems of shared state, which is the OP's main objective as I understand it. – miraculixx Apr 19 '15 at 17:13