Javascript Modules and Variable Scopes

Omar Barguti
4 min readApr 15, 2021

--

The Issue with Javascript Global Scope
Anyone with some background in programming knows that global variables are bad. Javascript is no exception. In fact, managing variable scopes in Javascript is even worse. This is particularly true in large enterprise applications where the application includes a large number of scripts where scope collision is very likely. Other languages such as Java and C# provide a namespace feature that mitigates the issues with cluttering the global scope and ensures that variable scopes are confined to the areas that they are intended to be. Unfortunately, such feature does not exist in Javascript. Luckily, there are a number of techniques and patterns that one can follow to prevent scope collision and ensure scalable Javascript code.

Creating a simple Javascript variable:
In the example below, a simple Javascript variable ‘executeTask’ is define using the keyword ‘var’. The value assigned to the variable is a function that logs to the console. The function is then invoked using parenthesis and everything works as intended

var executeTask = function() {
console.log("Executing a task");
}
executeTask();

While the code above works, it does present an issue in that the ‘executeTask’ variable is declared on the global scope. That is the case because the variable is not wrapped into any closures, or that is it not wrapped into any functions. One must remember that in Javascript, variable scopes are only enforced within functions and not in any other curly braces such as if statements or loops. What this means is that if another script declares a variable on the global scope called ‘executeTask’, it will collide with the variable declared above. One of the two declarations will end up overriding the other depending in the loading order of the scripts.

Reducing Clutter on Global Scope
Consider the situation where multiple ‘task’ related functions are to be declared. One could bundle all these functions in a single ‘module’ rather than declaring them directly on the global scope. Since Javascript does not natively support the notion of a ‘module’ of a name scape, native Javascript functions are used to simulate the notion of a module.

In the example below, a ‘module’ named ‘taskManager’ is declared and it nests two functions, executeTask and getTaskStatus. Since these two nested functions are declared using the ‘var’ keyword within the ‘taskManager’ module, they will not be accessible from the global scope. They must be accessed using the ‘.’ operator on the ‘taskManager’ module. In order for this pattern to work, the ‘taskManager’ function must ‘reveal’ all its nested functions by exposing them in the object returned by it.

var taskManager = function() {

var executeTask = function() {
console.log("Executing a task");
}

var getTaskStatus = function() {
console.log("Getting task status");
}

return {
executeTask: executeTask,
taskStatus: getTaskStatus
}
}
taskManager().executeTask();

In the example above, both local functions ‘executeTask’ and ‘getTaskStatus’ are added to the return object. Note that the name of the exposed property does not have to match the name of the function. In the example above, the ‘getTaskStatus’ was exposed under the name ‘taskStatus’. Typically, the names almost always are the same, but the do not have to be.

Immediately Invoked Functions
In the previous example, the declared functions were modularized and encapsulated into the ‘taskManager’ module. They can be easily accessed by invoking the ‘taskManager’ function first that returns and object revealing both nested functions. However, in this approach, one must always invoke the ‘taskManager’ function. Rather than having to do so every time a task function is to be invoked, Javascript supports immediately invoking the function upon declaration and storing the return object in the ‘taskManager’ variable. This way, the ‘taskManager’ function is no longer a function, it will be an object literal that does not require invocation.

var taskManager = function() {

var executeTask = function() {
console.log("Executing a task");
alert("Executing a task");
}

var getTaskStatus = function() {
console.log("Getting task status");
}

return {
executeTask: executeTask,
taskStatus: getTaskStatus
}
}();
taskManager.executeTask();

Immediately Invoked Function Expression (IIFE)
While all the approaches work, they still involve declaring a global variable. In the previous approach, a ‘taskManager’ global variable was declared on the global scope. This can be an issue if another Javascript is added to the HTML document and it declares another variable with the same name. In order to eliminate these variables altogether, Javascript allows wrapping the entire module with a function scope that is immediately invoked. These function wrappers are called Immediately Invoked Function Expressions (IIFE, pronounced “iffies”). Doing so will prevent adding any variables on the global scope. One would think that adding a pair of parenthesis will invoke the wrapper function. However, since it is not assigned to a variable, Javascript does not allow it. To fix this issue, the wrapping function must be wrapped in a pair of parenthesis

(function() {
var taskManager = function() {

var executeTask = function() {
console.log("Executing a task");
alert("Executing a task");
}

var getTaskStatus = function() {
console.log("Getting task status");
}

return {
executeTask: executeTask,
taskStatus: getTaskStatus
}
}();

taskManager.executeTask();

--

--

Omar Barguti

An enthusiastic architect and full-stack developer with many years working with enterprise software and cloud services in multiple domains.