Instructions: JavaScript Modules

If you are new to JavaScript, terms like "module bundlers vs. module loaders, "" Webpack vs.. Browserify ”and“ AMD vs.. CommonJS ”can make you 'surrender'.

JavaScript module system is quite scary, but it is the basic knowledge for any web developer.

In the article, we will decipher these common terms (along with a few more sample code). Hope you will gain a lot of knowledge from the article.

Part 1: Can you explain what module households are again?

Good writers will divide books into chapters and sections; Good programmers will divide the program into modules.

Like a story chapter, the module is simply a series of words (or code, depending on the case).

However, good modules are extremely self-contained with separate functions, allowing them to be scrambled, deleted, or added if needed, without disrupting the system.

Why use the module?

There are many benefits from using modules compared to complex, branching branch codebases. Some of the biggest benefits, I think, are:

  • Maintainability: By definition, the module is closed. A properly designed module will aim to solve the dependency in parts of the codebase as much as possible, from which the module can grow and improve independently. When the module is separated from other code, this module will be much easier to update.

Going back to the book example, when you want to change a chapter in a book, if a small change in the chapter needs to be fixed in other chapters too, this will quickly become a nightmare. Instead, you will want to write each chapter so that little (or not) must touch other chapters when you want to change something.

  • Namespacing: In JavaScript, the variable outside the high level function will be global (everyone can access it). Because of this, there will often be "namespace pollution", where the code (completely unrelated) shares global variables.

Sharing the global variable between unrelated code is something that really shouldn't happen in programming .

Deep into the article we will see that the module allows us to avoid namespace contamination by creating our own variable space.

  • reusability: honestly, one way or another, we all have copied previous written code into our new project. For example, imagine you copied some of the multi-use methods you wrote from the previous project to the current project.

Nice, but if you if you find a better way to write some parts of that code, you'll have to go back to the end and remember where you wrote to update.

This way of wasting a lot of time. If we have one … how good is the module that can be used again and again?

How do you combine modules?

There are many ways to link modules to the program. Let's take a look at some common methods:

Module pattern

The pattern pattern is used to simulate the concept of a class (because JavaScript does not support classes) so that we can store both public and private methods and variables in a single object – similar to the class used in other programming languages ​​(like Java or Python). This allows us to create mass APIs for methods that we want to roll out to global, but still attach private and methods variables in a closed scope.

There are many ways to implement pattern patterns. In the first example, I will use anonymous closure (anonymous closure), put all of our code into anonymous functions (Remember: in JavaScript, the function is the only way to create a new scope.)

Example 1: Anonymous packaging (Anonymous closure)

With this structure, our anonymous function will have its own evaluation environment or "closure" (closure), and then we will evaluate the function immediately. Thus, we can hide variables from the parent namespace (global namespace).

This is a good way that you can use local variables in this function without accidentally overlapping existing global variables, but still accessing the global variable, as follows:

Remember that round brackets around anonymous functions are mandatory, because statements (statements) that start with the keyword function are always treated as function declarations (remember, you can't have unnamed function declarations in JavaScript.) Therefore, the round brackets are around, instead, they will create function expressions. If you want to learn more, you can read here .

Example 2: Global import

global import is another quite famous method, used by libraries like jQuery . Similar to the anonymous wrapper we just saw, but won't move into global as the parameters (parameters):

In this example, globalVariable is the only global variable. This method has an advantage over anonymous wrappers, you can declare global variables from the beginning, which helps readers understand better.

Example 3: Object interface

One way to create another module is with an independent object interface, as follows:

As you can see, the method allows us to decide which variable / method to keep private (eg myGrades ) and which variable / method we want to expose, by placing the return statement (eg average & failing ).

Example 4: Revealing module pattern

Very similar to the method mentioned above, except all methods and variables will be kept private until expose explicitly:

It looks so much, but "unfortunately" this is just the tip of the pattern iceberg.

CommonJS and AMD

There is a common point in all of these methods: using a single global variable to encapsulate the code in a function, thereby creating a private namespace for itself with closure scope.

Although each method has its own effect, the defect is still unavoidable.

For example, as a programmer, you need to know the correct order of dependency to load the file into it. Assuming you are using Backbone for your project, you will add the tag script for the Backbone source code in the file.

However, since Backbone has a hard dependency on Underscore.js, the tag tag for the Backbone file cannot be replaced before the Underscore.js file.

As a programmer, managing dependencies in parallel with good handling of these tasks sometimes seems overwhelming.

Another shortcoming is that they can still lead to namespace conflicts. For example, what if there are two modules with the same name? Or if you have two versions of the same module, you need both?

You may be wondering: we can design a way to request the interface of the module without going through the global scope.

Fortunately, the answer is "yes". And we have two fairly well-known and optimal solutions: CommonJS and AMD.

CommonJS

CommonJS is a volunteer, design and integration team working with JavaScript APIs to declare variables.

A CommonJS module is actually a reusable JavaScript code, capable of exporting certain objects, helping other modules to require in their program. If you've ever programmed with Node.js, you should be familiar with this format.

With CommonJS, each JavaScript file stores the module in its own module context (just as the package is sealed). In this scope, we will use the module.exports object to expose modules, and require to import.

When you define a CommonJS module, you will often see:

We will use the special object module and set the function reference to module.exports . This allows the CommonJS module system to know which one we want to expose, so that other files can be consumed.

Then if someone wants to use myModule , they can require right in their file, as follows:

This solution has two obvious advantages compared to the pattern modules we discussed:

  1. Avoid full namespace pollution (global namespace pollution)
  2. Leave dependencies in explicit

Moreover, the syntax is also very neat, I really like this point.

Another feature to note is that CommonJS follows server-first direction and loads the synchronization module. The reason we need to note is that if we have three other modules that require require , we can only load one module at a time.

At this point, the server is great, but it is so awkward, it will be difficult to write JavaScript for the browser, reading modules from the web will take a lot more time than reading from the hard drive. As long as the script is to load the running module, the script will block the browser from running any other words until the module has finished loading. We see this behavior because the thread in JavaScript will stop until the code has finished loading.

AMD

CommonJS is nice and smooth, but what if we want to load the module asynchronously? The answer is Asynchronous Module Definition (ADM).

Load module with ADM will look like this:

We can see that function define receives an array of module dependencies as the first parameter. These dependencies are loaded in the background (in the non-block direction), and when loaded, define will call the callback function it has specified.

Next, the callback function takes the loaded dependencies as parameters – in our case, myModule and myOtherModule allow the function to use dependencies. Finally, the dependencies themselves must also be identified by the keyword define .

For example, the myModule might look like this:

So, again, different from CommonJS, AMD follows browser-first direction with asynchronous behavior to handle the job. Besides asynchronous properties, your module can also be object, function, constructor, string, JSON and many other data types, while CommonJS only supports objects as modules.

Thus, AMD is incompatible with the io, filesystem, and other server-oriented features included in CommonJS, and the function package syntax is more lengthy, compared to just a simple require statement.

UMD

For projects that require support for both AMD and CommonJS features, there is another format: Universal Module Definition (UMD).

UMD is to use one of the above two methods, while supporting defining global variables. From there, the module in UMD can work on both client and server.

The following is a brief example of how UMD:

For more vertical examples of UMD format, you can read here .

Native JS

Too much right, but not all. Because we still have another module type.

As you may have noticed, none of the above modules are available in JavaScript. Instead, we tried to emulate a module system through the use of pattern modules, CommonJS or AMD.

Fortunately, experts at TC39 introduced the built-in module with ECMAScript 6 (ES6).

ES6 offers a wide range of import and export modules capabilities, here are some of the resources you can look at:

Compared to CommonJS and AMD, the ES6 module is superior: the syntax is neat and clear, with asynchronous load capability, and advantages such as better cyclic dependencies support.

Perhaps my most favorite feature with ES6 modules is: preview directly exort in read-only format. (Compared to CommonJS, imports are a copy of export and therefore will not 'live'.)

The following is an illustrative example:

In this example, we basically create two module copies: an export copy, and a copy when require.

Moreover, the copy in main.js is now disconnected from the original module. That's why even if we increase the counter, it will still return 1 – because the counter variable we import is a disconnet copy of the counter variable from the module.

Thus, increasing the counter will increase the number in the module, but will not increase the copy version number. The only way to adjust the version copy of the counter variable is to do it manually:

On the other hand, ES6 creates a live preview (read-only) of the module we import:

Cool? A great feature of the live preview feature is that you can split the module into several pieces without losing any features.

You can then go back and merge again. There are no problems at all, so you can rest easy.

In the following section: bundling modules

So, we have just learned about the module. In the next section, we will explore the module in JavaScript more specifically, more specifically the bundling module (module package):

  • Why must we pack modules
  • Different package methods
  • ECMAScript API load module

Source: ITZone via Medium

Share the news now