المعرفة:: JavaScript الحالة::مؤرشفة المراجع:: The Complete JavaScript Course 2022 From Zero to Expert
What is a JavaScript Module?
- Reusable piece of code that encapsulates implementation details.
- Usually a standalone file, but it doesnt have to be. It can also have imports and exports.
- We can export values out of a module like simple values or entire functions. Whatever we export from a module is called the public API.
- This public API is actually consumed by importing values into a module.
Why JavaScript Modules should be used?
- Compose software: Modules are small building blocks that we put together to build complex applications.
- Isolate components: Modules can be developed in isolation without thinking about the entire code base.
- Abstract code: Implement low-level code in modules and import these abstractions into other modules.
- Organized code: Modules naturally lead to a more organized code base.
- Reuse code: Modules allow us to easily reuse the same code, even across multiple projects.
Native JavaScript (ES6) Modules
- ES6 Modules are modules stored in files, exactly one module per file.
ES6 Module | Script | |
---|---|---|
Top-level variables | Scoped to module (variables are private to the module by default) | Global (can cause global namespace pollution, where multiple scripts try to declare variables with same name then these variables collide) |
Default mode | Strict mode | Sloppy mode |
Top-level this | undefined | window |
Imports and exports | Yes Need to happen at top-level Imports are hoisted! | No |
HTML linking | <script type="module"> | <script> |
File downloading | Asynchronous | Synchronous |
Import and Export syntax
How ES6 Modules are Imported?
- 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.
- Modules are imported synchronously.
- 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.
- We can also export multiple variables at same time.
- We can change the name of the imported variable by using
as
keyword.
- This is also possible in the export statement as well.
- We can import all exports of a module at same time by using
*
in the import statement.
Default Exports
- It should be used when we only want to export one thing per module.
- We can mix named and default exports at same time. However in practice, we usually never mix Named and Default Exports in the same module.
Top-Level await in Modules
Top-Level await
- Starting from ES2022 version, we can now use the
await
keyword outside ofasync
functions, at least in modules (<script type="module">
).وصلة للملاحظة الرئيسة
- While this is all great and very useful, this actually blocks the execution of the entire module now.
- If one module imports a module which has a top-level
await
, then the importing module will wait for the imported module to finish the blocking code.
The Module Pattern
- 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.
CommonJS Modules
- 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 userequire
function of Node.js.