JavaSript manages scope by using functions. In JavaScript there are two types of scopes:
- Global Scope and
- Local Scope.
JavaScript uses functions as the primary way to define scope any variable defined inside of a function is in the local scope. A variable defined outside of a function is in the global scope.
Why Avoid JavaScript Globals
Global variables can be easily overwritten by other scripts which can result in unexpected things in our program. This becomes a bigger and bigger problem as we include more externally designed things in our program.
It’s really common to include things like:
- third party JavaScript libraries (jQuery, Bootstrap, etc)
- third-party analytics or user tracking scripts (Google Analytics, HotJar, etc)
- scripts from advertising partners
- maybe even widgets, badges, or buttons made by somebody else.
It’s impossible to completely avoid global scope in JavaScript, but it’s certainly possible to avoid abusing it! When we create a namespace it is technically global but it’s only one global variable instead of potentially dozens or hundreds.
Don’t Allow Undefined Variables
One of the most common ways of ending up with bugs is undefined variables. Here’s an example:
function addNumbers(x, y) {
// implied global variable
result = x + y;
return result;
}
If you aren’t running strict mode, this will create a new “result” variable in the global scope OR override an existing value if there’s already a “result” variable in the global scope.
This whole situation could be avoided by changing the function to be like this:
function addNumbers(x, y) {
var result = x + y;
return result;
}
We could simplify it even more by completely avoiding defining a variable at all. We really don’t need one for such a simple function:
function addNumbers(x, y) {
return x + y
}
And, it’s more pure. (See my post Functional Programming with JavaScript on why that’s a great thing!)
Avoiding Globals By Using Closures
In JavaScript, a closure is a function that has an inner function that can access the outer function’s variables. This effectively creates a scope chain that allows us to avoid adding a lot more variables to the global scope.
A really simple example of a closure would look like this:
function outer() {
var counter = 0;
function inner() {
counter++;
console.log(counter);
}
return inner;
}
Every time we referenced outer() the counter variable would increment. So far example in the below image I simply called a() a few times which incremented it 3 times.
Although the example above is pretty simplistic, we could certainly use something like that when we are doing a file upload counter.
Avoiding Globals By Using Function Properties
Object properties or function properties could be another way of avoiding using global variables. For example, we could do a bit simpler counter that doesn’t need the closure and doesn’t necessarily need to be declared again.
function doSomething() {
// handles files or something...
}
var uploadCounter = function () {
uploadCounter.value = uploadCounter.value || 0;
doSomething();
uploadCounter.value ++;
}
Effectively, every time we call uploadCounter() it would increment the value of uploadCounter.value which we could use elsewhere in the code if we needed to.
Global variables are natural, but they can be the cause of some really hard to debug problems that could potentially turn into security problems. Third-party libraries should never be able to unintentionally override your code.