JavaScript OOP Course 7: ES6 Modules
References
- Code with Mosh: The Ultimate JavaScript Mastery Series – Part 2
- W3School: JavaScript Tutorial
Modules
When our program grow it is hard to maintain one large file with hundreds or thousands lines of code. So we should split our code into multiple files and we call each of these files a Module. Modularity gives us a number of benefits:
- Maintainability of our application – because our code is better organized.
- This het a chance of reuse one or more of these modules in different part of our application or in different applications.
- We can abstract code – hide some of the complexity in a module and only expose the essentials.
index.js
const _radius = new WeakMap();
class Circle {
constructor(radius) { _radius.set(this, radius); }
draw() { console.log('Circle with radius ' + _radius.get(this)); }
}
const c = new Circle(10);
c.draw();
Despite in the above example we are using a WeakMap we still have access to the _radius
property in some ways – for example:
console.log(_radius.get(c));
10
If we put the above code in a separate module file and expose only the Circle
class we will no longer have access to the _radius
property as it is shown above – this is abstraction in practice.
In ES6 we don't have a concept of modules, so different solution emerged to solve this problem – smart developers in the community introduced new syntaxes to define modules. We refer to these syntaxes as module formats.
The popular modules syntaxes are:
- AMD – Asynchronous module definition – it is primary used in Browser applications,
- CommonJS – it is used in Node.js,
- UMD – Universal Module Definition – Browser/Node.js,
- ES6 Modules – the rest formats are used with ES5 and earlier, but now JavaScript has native support module format.
We can say AMD and UMD are now deprecated and in the following sections we will explain how CommonJS and ES6 Modules can be used.
CommonJS Modules
CommonJS Module Format is used in Node.js. The following example is created and started within Ubuntu on WSL on Windows 10 – see the section Install Node.js and NPM below.
The basic rule of thumb in modularity is: The things that are highly related should go together – this is what we called Cohesion in software engineering. By default everything that we define in a module is considered to be private. So it wont be accessible to the outside, unless we explicitly export it.
The way we export object in Node.js or in CommonJS format is like this:
class Circle {}
class Square {}
module.exports.Circle = Circle;
module.exports.Square = Square;
- The
module
keyword refers to the current module (file). - The property
.exports
is a object so we can add one or more properties to this object, like.Circle
,.Square
, etc. - Finally we set the
= Circle
or= Square
class to the dedicated property ofmodule.exports
- So in this certain example
module.exports
is an object with two properties.Circle
and.Square
In case we are exporting a single object we can simplify the above code in the following way:
class Circle {}
module.exports = Circle;
Instead of adding a .Circle
property to the module.exports
object, we just resetting that object to the Circle
class, so when we import the module we will get the Circle
class.
Based on this knowledge we can divide the code from the previous section in two files index.js
which is our main file and circle.js
which is our module file.
circle.js # This is our module
// Implementation details
const _radius = new WeakMap();
// Public interface
class Circle {
constructor(radius) { _radius.set(this, radius); }
draw() { console.log('Circle with radius ' + _radius.get(this)); }
}
module.exports = Circle;
The main file index.js
should use the require()
function of Nod.js in order to load our module. We will store the class exported by the module in a constant and then we will use that constant as regular class – just like before. Within the require()
function we need to pass the relative path to our module and its file name. In our particular case we need to pass ./circle.js
, note we can omit the file extension .js
, because it is assumed by default.
index.js # this is the main file
const Circle = require('./circle.js');
const c = new Circle(10);
c.draw();
The most interesting part here is – within the circle.js
we are exporting just the Circle
class, so the _radius
WeakMap is not accessible by the other modules – this is part of the implementation details of our module. This is abstraction in practice – we are hiding the details or the complexity inside of a module.
Now we can go in the terminal and test does our code works.
node index.js
Circle with radius 10
ES6 Modules
See also: W3School: JavaScript Modules
Here is how we can use ES6 Modules to achieve the same as in the above section but in Browser.
circle.js
// Implementation details
const _radius = new WeakMap();
// Public interface
export default class Circle {
constructor(radius) { _radius.set(this, radius); }
draw() { console.log('Circle with radius ' + _radius.get(this)); }
}
By default all in that file is considered private, unless explicitly export it bi the export
keyword. In the example above we using the export default
keyword right before our Circle
class definition, but it could be done at the end of the file like this:
class Circle {}
export default Circle;
When we import the circle.js
module we will have access to the exported class but we wont be able to work with the _radius
WeakMap…
In order to import our module we must use the import
keyword in our main file index.js
. as it is shown below. Note the file extension .js
can be omitted with this syntax too, because it is assumed by default.
index.js
import Circle from './circle.js';
const c = new Circle(10);
c.draw();
Just only for the demo (this is not production approach!) we need to change the script type to module within the index.htm
which is actually executed by the browser.
index.html # note type="module" within the <script> tag
<!DOCTYPE html>
<html lang="en">
<head>
<!-- head -->
</head>
<body>
<!-- body -->
<script type="module" src="index.js"></script>
</body>
</html>
# Result in the browser's console
Circle with radius 10 circle.js:8