Concepts: The JavaScript Module Design Pattern

In this topic, you will learn how to use the JavaScript Module Design Pattern to reduce the chance that your code will conflict with other scripts on your web page.

Using JavaScript variables

Before ES2015(ES6) release, JavaScript variables used to be declared using the var keyword. But, with the introduction of ES6, let and const were added as a new way to declare variables. This frequently raises questions about which keyword should be used and when.

Here is a short explanation of the different ways to declare a variable in JavaScript.

Using var

Using var is the oldest method of variable declaration in JavaScript. When defined within a function, any var is restricted to that function, but a var is global when defined outside of a function.

// Global Declaration
var variable = value;

const varExampleFunction = () => {
    // Local Declaration
    var variable = value;
}

Using let

The let declaration was introduced with ES6, this type of declaration is block-scoped, which means that the variables declared with let can be only accessed in the block or function in which it is defined.

const letExampleFunction = () => {
    let variable = value;
}

Using const

As let, const was also introduced with ES6. This is why both declarations are very similar. The main difference is that const points to data in memory that holds constant values, and const reference variables cannot be reassigned to a different object in memory.

 const constExampleFunction = () => {
    const variable = value;
}

Scope conflicts

In JavaScript, when you define a variable using the var, let or const element, it is scoped within the function that it is defined. Global variables are vulnerable to collisions with other scripts on your page.

Let's look at a code example. In the following code, the function and the variables exist within the scope of the page.

// script 1
const incrementCount = () => {
    count++;
}

const myButton = document.getElementById('buttonId');
let count = 0;

myButton.onclick = incrementCount;

Now, let's say there is a function outside of your script that also modifies the global count variable. This collision of scripts can cause unexpected results.

// script 2
const countVideos = videoList => {
    count = videoList.length;
}

Results:

  1. User selects the myButton button two times, incrementing the count variable in script 1.
    • count = 2
  2. The countvideos function is called which exists in Script 2, but also on your web page. Let's say that the videoList contains 10 items. Now, the count global variable has a value of 10.
    • count = 10
  3. The next time the user selects the myButton button, the count variable will return unexpected results.
    • Expected: count = 3
    • Actual: count = 11

You may try to avoid conflicts in your scripts, but there is no guarantee that third-party scripts included in your page will not use similar function and variable names.

Anonymous functions

One solution is to wrap your code in an anonymous function (also called a closure), that gets executed immediately. Your code within a closure is not accessible by other scripts. So, this gives you a way to create private functions and variables.

Here is the syntax for an anonymous function:

  • Line 3: includes an additional set of parentheses, which tells JavaScript to execute the function immediately after it has been parsed, instead of waiting for some other code to call the function.
( () => {
    // your code
}());

Another syntax example:

var res = function( [arguments] ) { ... }

Closures can be powerful, as they provide privacy and state throughout the lifetime of the application. For the code inside the closure, all variables and functions are in the closure scope only. But, your code inside the closure can still access any global variables or functions.

Globals

Although JavaScript has a feature known as implied globals, it may make your code hard to manage, as it is not easy to determine which variables are global. To determine if a variable is global, the interpreter has to walk backwards through the scope chain looking for a var statement that matches in name. If none is found, the variable is assumed to be global.

Pass in globals

With the anonymous function, you can explicitly pass in global parameters. This is called importing parameters into your code.

Here is an example:

  • Line 1: defines the name of the parameters being passed into the function. Notice that they do not have to match the names in line 3. Here, the window object is passed into a parameter named window1.
  • Line 3: passes the window object into the function.
( ( window1, undefined ) => {
    ...
})(window);

Since there is only 1 object being passed in, but there are two parameters, the value of undefined will be undefined.

typeof undefined == "undefined"

This can be handy if you want an easy way to check if other variables are defined.

if(variable1 === undefined)

Export globals

You may also want to pass variables and functions outside of your anonymous function. You can do this by using the return value.

Here is an example:

  • Line 1: assigns our anonymous function to BCLS. This value can be anything you choose. In this example, we are using BCLS (Brightcove Learning Services).
const BCLS = ( ( window1, undefined ) => {
    var object1 = {};
    object1.count = 1;
    object1.method = function () {
        ...
    }
    return object1;
})(window);

The object1 object is now available globally with two public properties, a variable named count and a function named method. They can be accessed outside of our anonymous function as:

  • BCLS.object1.count
  • BCLS.object1.method

Complete examples

Here are some complete examples of the JavaScript Module Design Pattern.

Example 1

This example shows how to create private and public variables and functions using the Module Pattern.

  • Private variables: myvar, myvar2
  • Private functions: fname, fname2
  • Public variable: myvar3
  • Public function: fname3
const BCLS = ( () => {
   var myvar = value,
       myvar2 = value;

   fname = () =>  {
        ...
   };
   fname2 = () =>  {
        ...
   };

   return {
        fname3 : () =>  {
             ...
        },
        myvar3 = value;
   };
}());

Example 2

This example passes in global objects and exposes a public function.

const BCLS = ( ( window, document, videojs ) => {
  var myvar = value;

  // use a global object passed into the anonymous function
  videojs.registerPlugin('overlay');

  fname = () => {
      ...
  }
  return {
    fname2 : () => {
          ...
      }
  }
})(window, document, videojs);

// call the public function fname2
var newvar = BCLS.fname2();

Code samples

Some of our code examples use the JavaScript Module Design Pattern, and you can review them for ideas on how to implement this pattern.