Introduction

The ES5 version of JavaScript does not support modules, but regardless of that it is possible to create applications in the manner of modular programming by following certain principles. Modules with their encapsulated code are created using “Immediately-Invoked Function Expressions” or with the constructor function. We simply return the API of the module that should be public inside a function with the reserved word return. This is a simple template and can be deployed anywhere without additional libraries. It is possible to define several modules within one file.
In addition to the above advantages, this approach has its disadvantages:
- “pollution” of the global domain with module names, although the body of the module is hidden using functions (IIFE) in the local domain.
- “manually” determining the loading order of modules and the loading order of modules can be quite complicated in large and complex applications.
- no asynchronous loading possible
These templates are not perfect and have their flaws, but regardless of the flaws, the code is more understandable because it is better organized.
IIFE – Immediately-Invoked Function Expressions
Definition
IIFE (pronounced “ifi”) is a function expression that is executed immediately after creation. It is created by wrapping the “function declaration” with a pair of parentheses and creating a “function expression”. The reason for transforming “declared function” to “function expression”, is that a declared function cannot be called immediately in the same statement while a function expression can.
|
1 |
( function(){} ); |
After creating the function expression in the same command, we add another pair of parentheses and thus immediately call the function.
|
1 |
( function(){} )(); |
Data privacy within the IIFE
IIFE is used for the modular programming pattern because all code inside such a function remains private, as seen in the following example.
|
1 2 3 4 5 6 7 8 |
(function(){ var firstName = "Dragoljub"; var jezik = "JavaScript"; var programer = firstName + " je " + jezik + " programer"; console.log(programer); })(); // Returns: "Dragoljub je JavaScript programer" console.log(programer); // Returns: Error programer is not defined |
Passing variables to IIFE
Global variables can be passed inside a function, when calling a function as an argument to that function:
|
1 2 3 4 5 6 |
(function(firstName, jezik){ var firstName = firstName; var jezik = jezik; var programer = firstName + " je " + jezik + " programer"; console.log(programer); })("Dragoljub", "JavaScript"); // Returns: "Dragoljub je JavaScript programer" |
The jQuery library also uses this approach to pass a variable:
|
1 2 3 |
(function ($) { // Now the jQuery global variable as $ is available to the code }(jQuery)); |
Return data
With IIFE we can choose which data we want to keep private and which data we want to make public. Those parts of the code that are returned with the return keyword will be publicly available. With the mentioned method, we can pass all types of data to the global namespace. In the following example, we return the variable and thus make it globally visible:
|
1 2 3 4 5 6 |
var oceneIspita = (function (nizOcena) { var ocene = nizOcena; return ocene; })([9, 5, 8, 9, 5, 7]); console.log(oceneIspita); // Returns: [9, 5, 8, 9, 5, 7] |
we can return the function
|
1 2 3 4 5 6 7 8 9 10 11 12 |
var proracunIspita = (function (nizOcena) { var ocene = nizOcena; function prosek () { var total = ocene.reduce(function(accumulator, item) { return accumulator + item; }, 0); return'Tvoj prosek je ' + total / ocene.length + '.'; } return prosek; })([9, 5, 8, 9, 5, 7]); console.log(proracunIspita()); // Returns: 'Tvoj prosek je 7.166666666666667.' |
Or we can return the entire object
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var proracunIspita = (function (nizOcena) { var ocene = nizOcena; var objekat = {}; objekat.prosek = function() { var total = ocene.reduce(function(accumulator, item) { return accumulator + item; }, 0); return'Tvoj prosek je ' + total / ocene.length + '.'; } return objekat })([9, 5, 8, 9, 5, 7]); console.log(proracunIspita.prosek()); // Returns: 'Tvoj prosek je 7.166666666666667.' |
Revealing module pattern
Most often, it is not necessary to return the entire object, but it is enough to reveal to the public only some parts of the code, which is implemented through the “Revealing module pattern”. With this principle, we can choose which methods we want to be private and which to be public and available to other modules.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
var proracunIspita = (function (nizOcena) { // AUDIENCE VARIABLES var ocene = nizOcena; // PRIVATE METHOD: function _brojPadova () { var padovi = ocene.filter(function(item) { return item < 6; }); return 'Pali ste ' + padovi.length + ' puta.'; } // PUBLIC METHOD: function prosek () { var total = ocene.reduce(function(accumulator, item) { return accumulator + item; }, 0); return'Tvoj prosek je ' + total / ocene.length + '.'; } // SELECTION OF PUBLIC METHODS return { ocene:ocene, prosek: prosek } })([9, 5, 8, 9, 5, 7]); console.log(proracunIspita.ocene); // [9, 5, 8, 9, 5, 7] console.log(proracunIspita.prosek()); // 'Tvoj prosek je 7.166666666666667.' console.log(proracunIspita._brojPadova()); // Returns: error "calcunSpita.brojPadova is not a function" |
IIFE + Singleton template

General
In software engineering, the singleton (Serb. unique) programming pattern is based on the principle that each object (class) has ONLY ONE INSTANCE, with the condition that access to that instance is globally available. In JavaScriptthis is possible by assigning IIFE to a global variable. Methods that we want to be globally available are “revealed” in modules following the “Revealing module pattern”. With IIFE, a local scope is created and pollution of the global domain is prevented, but the name of each module becomes part of the global domain and may collide with an external library if its variable has the same name.
Practical application in the application
index.html
This file forms the basis of the application and serves as a view for collecting data from the user and returning the results. Pay attention to the part responsible for loading modules, because the order of loading modules depends on the functionality of the application. This is the most difficult part for a developer, especially for large and complex applications. In this example, “input.js” is loaded first because its methods are used in both “proracun.js” and “app.js”, while the script “proracun.js” is loaded immediately after it but before “app.js” because its method is an integral part of “app.js”.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<html> <head lang="en"> <meta charset="UTF-8"> <title>Modular application</title> </head> <body> <nav class="navbar navbar-default"> <div class="container-fluid"> <div class="navbar-header"> <span class="navbar-brand">MODULARNA APLIKACIJA</span> </div> </div> </nav> <div class="form-horizontal" id="nameform"> <!-- UNOS PODATAKA: --> <div class="form-group"> <label for="entry" class="col-sm-2 control-label">Ulazni podaci</label> <div class="col-sm-2"> <input type="text" class="form-control" id="entry" size="20" placeholder="Unesite podatke" /> </div> </div> <!-- DUGME ZA STARTOVANJE APLIKACIJE (CALCULATION): --> <div class="form-group"> <div class="col-sm-offset-2 col-sm-10"> <button class="btn btn-success" id="calculate">Calculation</button> </div> </div> </div> <!-- SEKCIJA ZA RETURN REZULTATA: --> <div class="col-sm-10" id="scores"> <h3>REZULTAT:</h3> <div id="result"> <p>Calculate iznos je: <span></span></p> </div> </div> <!-- SEKCIJA ZA UČITAVANJE MODULA --> <script src="js/unos.js" type="text/javascript"></script> <script src="js/proracun.js" type="text/javascript"></script> <script src="js/app.js" type="text/javascript"></script> </body> </html> |
input.js
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var unos = function() { // PRIVATE VARIABLE var unesenPodatak = ''; // A FUNCTION THAT ACCEPTS INPUT function setPodatak(noviPodatak) { unesenPodatak = noviPodatak; } // A FUNCTION THAT PROCESSES INPUT function getpodatak() { return unesenPodatak; } // PUBLIC DISCLOSURE OF METHODS return { setPodatak: setPodatak, getpodatak: getpodatak }; }(); |
budget.js
|
1 2 3 4 5 6 7 8 9 10 11 |
var proracun = function() { function calculateScore() { // METHOD CALL FROM unos.js unos.getPodatak(); // goes here part of the code related to BUDGET } // PUBLIC DISCLOSURE OF METHOD return { calculateScore: calculateScore, }; }(); |
app.js
|
1 2 3 4 5 6 7 8 9 10 11 |
(function() { // "click handler" FOR DATA ENTRY (calls the function from the module unos.js) document.getElementById('entry').addEventListener('change', function() { unos.setPodatak(document.getElementById('entry').value); }); // "click handler" TO START THE CALCULATION (calls the function from proracun.js) document.getElementById('calculate').addEventListener('click', function() { proracun.calculateScore(); }); })(); |
Template constructor
General
The constructor template allows creating multiple instances based on the constructor function. The code changes compared to the previous template are minor. The body of the function remains unchanged while only the following things are adjusted:
- The name of the variable to which the function is assigned should start with an uppercase letter, to satisfy the convention
- IIFE is transformed into a regular function, by deleting the parentheses that call the function immediately because the standard constructor function is planned to be called only with the reserved word “new”
- Inside a module that requests a method from another module, a new object needs to be instantiated, so that its method is available.
Practical application in the application
input.js
This module does not use methods from other modules, so the changes are only related to removing function call brackets.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var Unos = function() { // PRIVATE VARIABLE var unesenPodatak = ''; // A FUNCTION THAT ACCEPTS INPUT function setPodatak(noviPodatak) { unesenPodatak = noviPodatak; } // A FUNCTION THAT PROCESSES INPUT function getpodatak() { return unesenPodatak; } // PUBLIC DISCLOSURE OF METHODS return { setPodatak: setPodatak, getpodatak: getpodatak }; }; |
budget.js
Inside this module it is necessary to instantiate a new “Input” object.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
var Proracun = function() { function calculateScore() { // METHOD CALL FROM unos.js var unos = new Unos(); // ADD CODE HERE unos.getPodatak(); // goes here part of the code related to BUDGET } // PUBLIC DISCLOSURE OF METHOD return { calculateScore: calculateScore, }; }; |
app.js
Inside this module it is necessary to instantiate a new “Input” and “Calculation” object.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
(function() { // "click handler" FOR DATA ENTRY (calls the function from the module unos.js) document.getElementById('startGame').addEventListener('click', function() { var unos = new Unos(); // ADD CODE HERE unos.setName(document.getElementById('entry').value); }); // "click handler" TO START THE CALCULATION (calls the function from proracun.js) document.getElementById('calculate').addEventListener('click', function() { var proracun = new Proracun(); // ADD CODE HERE proracun.calculateScore(); }); })(); |
