# Modules
# Why use modules?
It makes sense to split JavaScript programs up into separate modules that can be imported when needed.
There are a lot of benefits to using modules:
1) Maintainability: Updating a single module is much easier
2) Namespacing: Sharing global variables between unrelated code is a big no-no in development (opens new window). Modules allow us to avoid namespace pollution by creating a private space for our variables.
3) Reusability
Modern browsers support module functionality natively.
# What is a module? (opens new window)
Modules are reusable pieces of code in a file that can be exported and then imported for use in another file.
-βΊ separation of concerns
Modules can load each other and use export
and import
to interchange functionality, call functions of one module from another one:
export
keyword labels variables and functions that should be accessible from outside the current moduleimport
allows the import of functionality from other modules.
// π sayHi.js
export function sayHi(user) {
alert(`Hello, ${user}!`);
}
another file may import and use it:
// π main.js
import {sayHi} from './sayHi.js';
sayHi('John'); // Hello, John!
# Import in HTML
type="module"
is nescessary, otherwise it won't work
<script type="module">
import { sayHi } from './sayHi.js';
document.body.innerHTML = sayHi('John');
</script>
or
<script src="app.js" type="module"></script>
Modules work only via HTTP(s), not locally -> Use a local web-server like "live-server"
# Module-level scope
Each module has its own top-level scope -> each module has independent variables
Modules should
export
what they want to be accessible from outside andimport
what they need.Use import/export instead of relying on global variables.
# A module code is evaluated only the first time when imported
If the same module is imported into multiple other modules, its code is executed only once, upon the first import. Then its exports are given to all further importers.
# In a module, βthisβ is undefined (opens new window)
<script>
alert(this); // window
</script>
<script type="module">
alert(this); // undefined
</script>
# import/export
- Syntax- (ES6 / ESM)
Syntax for natively implementing modules was only introduced in 2015 with the release of ECMAScript 6 (ES6) (opens new window).
ESM stands for ES Modules.
- Works in many modern browsers (opens new window)
- It has simple syntax and async
- Tree-shakeable (opens new window), due to ES6's static module structure (opens new window)
- ESM allows bundlers like Rollup to remove unnecessary code (opens new window), allowing sites to ship less codes to get faster load.
# ES6 Import Syntax
import { exportedResourceA, exportedResourceB } from '/path/to/module.js';
you must also update the html: add the attribute type='module'
to the <script>
element.
<script type="module" src="./secret-messages.js"> </script>
</body>
# Renaming Imports
-> to Avoid naming Collisions
ES6 includes syntax for renaming imported resources by adding in the keyword as
.
import { exportedResource as newlyNamedResource } from '/path/to/module'
// index.js
import { ninjas, greet } from './module.js'
console.log(ninjas);
greet()
or
import * as module from './module.js';
console.log(module.ninjas);
module.greet();
# Import entire content
import * as myModule from '/modules/my-module.js';
call it like this:
myModule.functionFromTheModule();
# Import a single export
import {myExport} from '/modules/my-module.js';
# Import multiple exports
import {foo, bar} from '/modules/my-module.js';
# Import with alias
import {reallyReallyLongModuleExportName as shortName}
from '/modules/my-module.js';
import {
reallyReallyLongModuleExportName as shortName,
anotherLongModuleName as short
} from '/modules/my-module.js';
# ES6 Named Export Syntax
The name of each exported resource is listed between curly braces and separated by commas
export { resourceToExportA, resourceToExportB, ...}
individual values may be exported as named exports by placing the export
keyword in front of the declaration.
export const toggleHiddenElement = (domElement) => {
// ...
}
export const changeToFunkyColor = (domElement) => {
// ...
}
# Examples
// Exporting individual features
export let name1, name2, nameN; // also var, const
export function functionName(){...}
export class ClassName {...}
// Export list
export { name1, name2 };
// Renaming exports
export { variable1 as name1, variable2 as name2 };
// Exporting destructured assignments with renaming
export const { name1, name2: bar } = object;
// ninjas.js
export const ninjas = ['shaun', 'yoshi', 'mario', 'peach'];
export const greet = () => {
console.log(ninjas[0] + ' says hello');
};
or:
// ninjas.js
const ninjas = ['shaun', 'yoshi', 'mario', 'peach'];
const greet = () => {
console.log(ninjas[0] + ' says hello');
}
export { ninjas, greet }
You can also rename named exports to avoid naming conflicts:
export { myFunction as function1,
myVariable as variable };
# Default Exports
Every module also has the option to export a single value to represent the entire module called the default export.
can be an object containing the entire set of functions and/or data values of a module.
syntax :
as default
renames the exported object todefault
, a reserved identifier that can only be given to a single exported value.import can be any name
only 1 default export!
const resources = {
valueA,
valueB
}
export { resources as default };
shorthand syntax:
const resources = {
valueA,
valueB
}
export default resources;
or:
export default {
valueA,
valueB
}
// Default exports
export default expression;
export default function (β¦) { β¦ } // also class, function*
export default function name1(β¦) { β¦ } // also class, function*
export { name1 as default, β¦ };
// module "my-module.js"
export default function cube(x) {
return x * x * x;
}
import cube from './my-module.js';
console.log(cube(3)); // 27
# Default Import
import importedResources from 'module.js';
Notice that the import statement has no curly braces . This syntax is shorthand for:
import { default as importedResources } from 'module.js
if the default
export is an object, the values inside cannot be extracted until after the object is imported, like so:
// This will work...
import resources from 'module.js'
const { valueA, valueB } = resources;
// This will not work...
import { valueA, valueB } from 'module.js'
Examples
import myDefault from '/modules/my-module.js';
combined:
import myDefault, * as myModule from '/modules/my-module.js';
// myModule used as a namespace
or
import myDefault, {foo, bar} from '/modules/my-module.js';
// specific, named imports
Example:
/* dom-functions.js */
const toggleHiddenElement = (domElement) => {
//...
}
const changeToFunkyColor = (domElement) => {
//...
}
const resources = {
toggleHiddenElement,
changeToFunkyColor
}
export default resources;
->
// secret-messages.js
import domFunctions from '../modules/dom-functions.js';
const { toggleHiddenElement, changeToFunkyColor } = domFunctions;
const buttonElement = document.getElementById('secret-button');
const pElement = document.getElementById('secret-p');
buttonElement.addEventListener('click', () => {
toggleHiddenElement(pElement);
changeToFunkyColor(buttonElement);
});
- Can be called in HTML:
<script type="module">
import {func1} from 'my-lib';
func1();
</script>
# Import a module for its side effects only (opens new window)
Import an entire module for side effects only, without importing anything. This runs the module's global code, but doesn't actually import any values.
import '/modules/my-module.js';
This works with dynamic imports (opens new window) as well:
(async () => {
if (somethingIsTrue) {
// import module for side effects
await import('/modules/my-module.js');
}
})();
# Dynamic Imports (opens new window)
# require
- Syntax (Node - CJS)
- node uses CJS module format (opens new window).
- CJS imports module synchronously
- When CJS imports, it will give you a copy of the imported object.
- CJS will not work in the browser.
- modern versions of node can also use
import/export
https://www.sitepoint.com/understanding-module-exports-exports-node-js/ (opens new window)
# Export: module.exports
To make functions available to other files, add them as properties to the built-in module.exports
object:
module.exports
is an object that is built-in to the Node.js runtime environment. Other files can now import this object, and make use of these functions, with the require()
function.
/* converters.js */
function celsiusToFahrenheit(celsius) {
return celsius * (9/5) + 32;
}
module.exports.celsiusToFahrenheit = celsiusToFahrenheit;
module.exports.fahrenheitToCelsius = function(fahrenheit) {
return (fahrenheit - 32) * (5/9);
};
or
module.exports = celsiusToFahrenheit
function requestHandler(req, res) {
// ...
}
// or:
// const requestHandler = (req, res) => {
// // ...
// }
module.exports = requesthandler
or to use as: modulename.handler
module.exports = {
handler: requestHandler,
someText: 'Some hard coded text'
}
or
module.exports.handler = requestHandler;
module.exports.someText = 'Some hard coded text'
or shortcut:
exports.handler = requestHandler;
exports.someText = 'Some hard coded text'
# Function directily in the Object:
module.exports = {
circleArea: (radiusLength) => {
return PI * radiusLength * radiusLength
},
squareArea: function (sideLength) {
return sideLength**2
}
}
or
module.exports = {
circleArea (radiusLength) {
return Math.PI * radiusLength * radiusLength
},
squareArea (sideLength) {
return sideLength**2
)
}
# Import: require()
Use require
-> node will look for module.exports
The require()
function accepts a string as an argument. That string provides the file path (opens new window) to the module.
/* water-limits.js */
const converters = require('./converters.js');
./
is a relative path indicating that converters.js is stored in the same folder as water-limits.js. When you use require()
, the entire module.exports
object is returned and stored in the variable converters
.
All exported methods can be used by converters.methodName
# Examples:
const module = require('./module.js')
const http = require('http')
const routes = require ('./routes,js') // custom module
const server = http.createServer(routes)
//-> will use the function stored in routes for incoming requests
server.listen(3000)
# Object Destructuring with require()
You can use object destructuring to extract only the needed functions.
/* celsius-to-fahrenheit.js */
const { celsiusToFahrenheit } = require('./converters.js');
const fahrenheitValue = celsiusToFahrenheit(input);
-> you can access the function directly
# Module-links:
https://dev.to/iggredible/what-the-heck-are-cjs-amd-umd-and-esm-ikm (opens new window)
https://blog.risingstack.com/node-js-at-scale-module-system-commonjs-require/ (opens new window)
https://dev.to/bennypowers/you-should-be-using-esm-kn3 (opens new window)
MDN JS Modules (opens new window)
β Spread... and ...Rest Play audio β