The first step is to parse the code (reading it without executing it). This is the moment in which imports are hoisted.
Modules are imported synchronously.
This is the easiest way to do things like bundling and dead code elimination.
By knowing all dependencies between modules before execution, bundlers like webpack and Parcel can then join multiple modules together and eliminate that code.
Possible thanks to top-level (“static”) imports, which make imports known before execution.
This makes bundling and dead code elimination possible.
After the parsing process and figuring out which modules needs to be imported, these modules are actually downloaded from the server asynchronously.
After a module arrives, it’s also parsed and the module’s exports are linked to the imports in importing file.
This is a live-connection, when the value changes in the exporting module, then the same value also changes in the importing module.
Exported values are not copied to imports, instead, the import is basically just a reference to the export at value like a pointer.
Finally, the imported modules is executed.
Exporting and Importing in ES6 Modules
In ES modules, there are two types of exports, Named Exports and Default Exports.
Named Exports
Named Exports is actually the simplest way of exporting something from a module, because all we have to do is to put export in front of anything that we might want to export, but it must be in the top level.
To import a named export, name of the imported variable must be written exactly the same between curly braces in the import statement.
Before ES6, the module pattern was used in order to implement modules in JavaScript.
The main goal of the module pattern is to encapsulate functionality, to have private data, and to expose a public API.
The best way of achieving that is by using a function, because functions give us private data by default and allow us to return values, which can become our public API. IIFE is very suitable for this kind of things because we don’t have to call it separately and we can also ensure that it’s only called once.
The stored function returned value can still get access to function variables because of closures.
The problem with this way is that if we wanted one module per file, we would have to create different scripts and link all of them in the HTML file. This creates a couple of problems:
Taking care of the order of script links in HTML.
Having all variables living in the global scope.
The inability of bundling them together using a module bundler.
Besides native ES Modules and the module pattern, there are also other module systems, that have been used by JavaScript in the past. They were not native JavaScript, so they relied on some external implementations. Two examples are: AMD Modules, and CommonJS modules.
CommonJS modules are important because they have been used in Node.js, for almost all of its existence.
Just like ES6 modules, in CommonJS, one file, is one module.
To export something from a module, we use export., and then the name of the export. While to import something we use require function of Node.js.
// CommonJS Modules// Exportexport.addTocart = function (product, quantity) { cart.push({ product, quantity }); console.log( `${quantity} ${product} added to cart (sipping cost is ${shippingCost})` );};// Importconst { addTocart } = require('./shoppingCart.js');