Journey to Angular Development: ES6 features – Part 4

Complete series source code can be downloaded from GitHub.

Introduction

We are in the middle of our journey to Station 2 from Station 1.

Are the words, Station 1 and Station 2 confusing you? It means that you are not following this series from the beginning.

This article is part of a series called “Journey to Angular Development”. We imagine this series as a train journey. Every time we reach a new Station, we become competent in something.

So far, we have reached Station 1 and are on the way to Station 2.

In this article, we will explore Modules in ES6.

Complete series (Index)

— — — — — — — — — — — — — — — — — — — — — — — — — —— — —

https://www.amazon.in/Journey-Angular-Development-Sukesh-Marla/dp/9391030149

— — — — — — — — — — — — — — — — — — — — — — — — — —— — —

JavaScript Modules

What are the Modules?

Almost every technology supports modules somehow. What is a module? Well, I will say, Module represents a small piece of an entire application.

Why we need Modules?

For now, let’s not worry about how to create modules. First, let’s focus on how much modular development is essential.

Here are the advantages of modules.

  • Modular development
  • Isolated coding
  • Dependency management

Modular development

Breaking applications into multiple modules makes it more manageable and readable.

Isolated coding

Every code we write belongs to some module and will be unaffected by every other code belonging to some other module. The code written inside each Module will be private by default to that Module. It won’t be accessible outside (to other modules). In case we want to make something available, it should be done explicitly.

So, a developer working on one Module won’t be worried about code written in another module. He/she won’t be worried that he will replace/overrides someone else’s functionality, or someone else will replace/overrides his functionality.

Dependency management

Let’s say we are developing an application, and our app needs a reusable module (module1) for some purpose. Now, let’s say module1 is dependent on two other modules (module2 and module 3). Finally, module 2 is dependent on module4.

To make module1 work properly, we must make sure that all other dependent modules (directly or indirectly), that is, module2, module3, and module4 also included in the application. In short, we need to know the entire dependency hierarchy. It is not the only challenge here. We also need to make sure that modules are included in proper order. In this case, we should include module4 first and then module2 and module 3 and finally module1.

Quite a difficult job, isn’t it?

The truth is that it won’t be the case in the case of modules. Your application will need a direct reference to module1, and everything else will be indirectly referenced. That’s the advantage of modules.

How to create Modules in JavaScript?

Now that you know why we need modules and as we are learning ES6, let see how to create modules in JavaScript.

It depends on your runtime environment

  1. If it is the node
  2. If it is a browser without support for ES6 Modules
  3. If it is a browser with support for ES6 Modules.

Let’s see all three areas one by one.

If it is the node

If you are going to use NodeJS runtime for executing your JavaScript, then every file you create will be considered as one Module. By default, every function and variable declared in one file will be hidden from another file. We must explicitly export things we need to make available outside.

(Just for your information, NodeJS internally uses the CommonJS module system. No need to worry about CommonJS as an Angular or as an ES6 learner)

Let’s do a demo.

Create a JavaScript file “lib1.js” and “lib2.js” as follows.

//lib1.js
function getString(){
    return getString2();
}

function getString2(){
    return "Just Compile";
}

It will create a new module called “lib1” internally.

//lib2 .js
function getString(){
    return getString2();
}

function getString2(){
    return "Train IT";
}

It will create a new module called “lib2” internally.

As you can see, both files contain functions with the same name, “getString” and “getString2”.

Next, create one more JavaScript file “test.js” with simple code as follows.

console.log(getString());

It will create a new module called “test” internally.

Execute “test.js” using the “node” command.

As you can see, we got a runtime error as there is no method in “test.js” with the name “getString” and “test” module cannot access the “getString” method defined in “lib1” and “lib2” module.

As I said before, we need to explicitly export things in every file which we want to make available outside. It can be done using the “exports” keyword.

Simply add the following line at the end of both “lib1.js” and “lib2.js”.

exports.myFunction=getString;

The above line says, out of two methods defined inside “lib1.js” and “lib2.js,” only “getString” will be available outside and that too with a different name “myFunction.”

In “test.js,” we cannot merely access stuff exported by other modules. We need to import that Module first, and for that, we use the “require” keyword.

Change “test.js” contents to the following.

let lib1=require('./lib1');
let lib2=require('./lib2');

console.log(lib1.myFunction());
console.log(lib2.myFunction());

As you can see, we are importing both “lib1” and “lib2” modules separately, and local variables “lib1” and “lib2” will give us access to the function exposed inside respective modules.

Execute it once again using the “node” command.

Now let’s do one more thing before wrapping up this demo.

Create three more JavaScript files and call it “lib3.js”, “lib4.js” and “test.js”. Put following contents inside it.

//lib3
let lib4=require("./lib4");
function getString(){
    return lib4.getString();
}
exports.getString=getString;
//lib4
function getString(){
    return "Sukesh Marla";
}
exports.getString=getString;
let lib3=require('./lib3');
console.log(lib3.getString());

As you can see, “test2.js” is only referring to “lib3.js” it is completely unaware of “lib4.js”.

Execute “test2.js” using the “node” command to get the following output.

From the above two demos, we can conclude the following points.

  1. Modules broke application into multiple small chunks, each encapsulating a particular functionality and thus achieving abstraction.
  2. Functionalities are overriding each other. The code written inside one Module is private to that Module. No way other modules can replace it.
  3. We are not required to remember the entire dependency tree. It has automatically taken care.

If it is a browser without support for ES6 Modules

Unfortunately, browser runtime doesn’t have support for built-in JavaScript modules (at least not before ES6 is introduced, we will talk about ES6 modules in the third point).

Let’s prove it with the help of a demo.

Create three JavaScript files, “lib1.js”, “lib2.js”, “test.js” and “index.html” as follows.

// lib1.js
function getLib1Message(){
    return getMessage();
}

function getMessage(){
    return "Just Compile";
}
// lib2.js
function getLib2Message(){
    return getLib1Message() +" & "+ getMessage();
}

function getMessage(){
    return "Train IT";
}
//test.js
alert(getLib2Message());
<html>
<head>
    <title>
        JavaScript Module Demo
    </title>
    <script src="lib1.js"></script>
    <script src="lib2.js"></script>
    <script src="test.js"></script>
</head>
</html>

We need a development purpose server where we can host above HTML and JavaScript files.

For that, we will be using “lite-server.” Open a command prompt and execute the following command.

npm install lite-server -g

After that, execute the “lite-server” command in the folder where the above files are saved.

It will automatically open the browser, requesting to “index.html” file. If it won’t, do it manually.

Aren’t you expecting output “Just Compile & Train IT”?

In the above case, “lib2.js” replaced the “GetMessage” method in “lib1.js”. It proves by default browser runtime doesn’t support modular system.

We don’t have default support, but we can bring in using “Module Loaders” and “Module Formatters.”

(Please note, this entire series is about “Angular.” We are discussing and covering all these terminologies just to make our base stronger. “Module Loaders” and “Module Formatters” are itself a vast topic. Don’t get into the depth of it for now.)

Module Formatters

It’s the standard way of writing code. AMD, CommonJS, UMD are some examples of “Module Formatters.” For our demo, we will be using the AMD format.

So, in a first step, let’s rewrite all three JavaScript files in AMD format.

Let’s start with “lib1.js”.

When it comes to writing code in AMD standard first thing we must do is invoke “define” function.

It accepts two parameters first of type array and second a function.

//lib1.js
define([], function() {

});

I firmly believe the first thing which must have come to your mind was, “what the hell is this define? I never saw it before in JavaScript.” 😊

Keep that thought aside for now and focus on demo. You will eventually understand it.

Next, write every code of yours inside that anonymous function.

define([], function() {
    function getLib1Message(){
        return getMessage();
    }
    function getMessage(){
        return "Just Compile";
    }
});

With the above code, we created a JavaScript module called “lib1.”

Secondly, as both “getLib1Message” and “getMessage” are defined inside another function, they become private to that function, and there is no way someone overrides it, or it overrides someone else in outside.

But if you remember our previous demo, then “getLib1Message” is invoked by “lib2,” which is not part of this Module. Then, how we make “getLib1Message” exposed publicly in the way, it will be still part of “lib2”.

For that, we will use the “exports” module. In your “lib1” module, you will get access to the “exports” module. That’s where the first parameter in define function comes to picture. Look at the following code.

//lib1.js
define(['exports'], 
function(e) {
    function getLib1Message(){
        return getMessage();
    }
    function getMessage(){
        return "Just Compile";
    }
    e.getMessage=getLib1Message;
});

Earlier “define” now “exports,” what’s going on here. Relax and proceed.

Let’s analyze the above code.

  • That array specifies other modules on which your Module is dependent.
  • In the above code, “exports” is a module name whereas “e” is the parameter of anonymous function. It can be anything other than “e.” It ultimately gets the reference to the “exports” module.
  • The above code indicates that “getLib1Message” will be available to other modules as “getMessage.”

Now that we are done with “lib1.js” let’s proceed with “lib2.js”.

// lib2.js
define(['exports',"./lib1.js"], 
function(e,lib1) {
    function getLib2Message(){
        return lib1.getMessage() +" & "+ getMessage();
    }
    function getMessage(){
        return "Train IT";
    }
    e.getMessage=getLib2Message;
});

As you can see, this time we not only have specified “exports” but also “./lib1.js” as a dependency. We want both. With “exports,” we make “getLib2Message” available in another module, and with “lib1,” we get access to the “getMessage” method of “lib1.js”.

Next, lets modify “test.js”.

define([
    './lib2.js',
], function(lib2) {
    alert(lib2.getMessage());
});

In “test.js,” we only have “./lib2.js” as a dependency.

Module loaders

They are third party libraries that make the browser understand “Module Formatters.” “requireJS” and “systemJS” are some examples of module loaders.

In simple terms, your browser will not recognize “define,” “exports” in the above code. We have to use “Module loaders” for that.

So’ let’s download one using npm.

npm install requirejs

Above command, download the “requireJS” module loader and keep it in the “node_modules” folder.

Now that we have everything ready, lets do our final change. Let’s change “index.html”.

<html>
<head>
    <title>
        JavaScript Module Demo
    </title>
    <script data-main = "test.js" src = "\node_modules\requirejs\require.js"></script>
</head>
</html>

As you can see, we have only included “require.js” in our HTML file and we have attached a special attribute to our script tag “data-main” which is set to “test.js”.

Now, remember “test.js” internally refers to “lib1.js” so that will get loaded dynamically into HTML file by “Module Loader,” and that leads to loading of “lib2.js” ultimately.

Execute the “lite-server” command in the folder where all the above files are kept and restart the server.

This time we will get the correct output.

It was a demo using “AMD” as a “Module Formatter” and “requireJS” as “Module Loader.” You can try some other combination as well. The purpose of this demo was to realize the importance and fundamentals of JavaScript modules.

If it is a browser with support for ES6 Modules

So far, we have learned that “NodeJS” runtime by default support for modules and “Browser runtime” won’t. The good news is this is the case with ES5. ES6 has support for “JavaScript Modules.”

So, if your browser is compatible with ES6 “JavaScript Module,” you can get out of the complexity of “Module Formatters” and “Module Loaders.” You can follow a standard syntax of the ES6 Module.

Already modern browsers started supporting ES6 “JavaScript Modules.”

For our demo, we will be using “Google chrome,” which has complete support for it.

So, without wasting time let’s start creating “lib1.js”, “lib2.js” and “test.js” just like before.

//lib1.js
export function getLib1Message(){
    return getMessage();
}
function getMessage(){
    return "Just Compile";
}

Did you notice the “export” keyword attached to the “getLib1Message” method? It means, it “lib1.js” module “getLib1Message” will be available to other modules.

// lib2.js

import {getLib1Message} from "./lib1.js"
export function getLib2Message(){
    return getLib1Message() +" & "+ getMessage();
}
function getMessage(){
    return "Train IT";
}

Look at the “import” statement. It’s intuitive.

We will dig more into “export” and “import” after this demo. For now, let’s get our initial output.

//test.js
import {getLib2Message} from "./lib2.js"
    alert(getLib2Message());

The code is much simpler and readable.

One last thing lets create “Index.html” file.

<html>
<head>
    <title>
        JavaScript Module Demo
    </title>
    <script type="module" src = "test.js"></script>
</head>
</html>

Did you notice, there is no “Module Loader” involved? We don’t need. Its ES6 and it’s a standard syntax. Also, you can see a special attribute value “module” to “type” attribute. It means “test.js” is a JavaScript Module.

Now its time for execution. Execute the “lite-server” command in the folder where the above files are kept and start the server. Here is the output.

Isn’t it cool? It is without any doubt.

Before we wrap this article, let’s learn a little more about these “ES6 Modules.”

More on ES6 Modules

Can we ES6 Modules with NodeJS?

With NodeJS version 13, we can use ES6 module syntax without any problem.

Can we export multiple things from one ES6 Module?

Yes, using same syntax we can do that.

//module1.js
export function a(){
    ...
}
export function b(){
    ...
}
function c(){
    ...
}

//module2.js
import {a,b} from "./module1.js"

Can we export variables?

Yes, check the below syntax.

//module1.js
export const ABCD=10;
//module2.js
import { ABCD } from "./module1.js"

What if the imported functionality already exists?

We can put alias while importing. Check the below syntax.

//module1.js
export function a(){
    ...
}

//module2.js
import { a as mya } from “./module1.js”

function a(){
    mya();
}

What if I want to everything exported by a Module but don’t want explicitly each in import statement?

You can use “*” as follows.

//module1.js
export function a(){
    ...
}
export function b(){
    ...
}
function c(){
    ...
}

//module2.js
import * as lib2 from "./lib2.js"
console.log(lib2.a());
console.log(lib2.b());

Is there any other way to export?

The export syntax used in the above demos is called “Named export.”

There is something called as “Default export”. Check the below code

//module1.js
export default function a(){
    ...
}
export function b(){
    ...
}
// module2.js
import xyz,{b} from "./module1.js"
function m1(){
    return xyz();
}
function m2(){
    return b();
}

In the above code, “module1” is having both named export (function b) and default export (function a).

  • Named exports must be specified “inside” “{” and “}” whereas default exports without it.
  • Only one default export is possible per module
  • We can use an alias with default exports. In the above case, “function a” is imported as “function xyz.”

Conclusion

So, with JavaScript Modules, we finish our journey to Station 2. In the next article, we will start our trip to Station 3

I hope you have enjoyed this writing.

Stay tuned and practice well. Please drop comments and clap. It helps us to stay motivated to write more such series.

You can check me out on twitter @sukeshmarla.

In case you are looking for customized mobile or web application development, exclusive technical Consultancy for architecting your project, and want to train your people in advanced technologies, you can visit my company Just Compile or contact SukeshMarla@Gmail.com for details.

Sending
User Review
5 (3 votes)

You May Also Like

Avatar

About the Author: Sukesh Marla

I am a passionate programmer and a founder of Just Compile. My company is specialized in consulting, customized application development, and training. You can find me on twitter @sukeshMarla

Leave a Reply

Your email address will not be published. Required fields are marked *