Introduction to javascript module

Posted by doublebassdanny on Tue, 21 Dec 2021 15:29:51 +0100

With the emergence of ES6, js module has become a formal standard. The folk scripts, requireJs(AMD), SeaJs(CMD) and Node(CommonJs), once developed to solve the problem of js module, have become history or will become history in the near future. It is also important to understand history, because formal standards are developed based on folk secrets, and some specifications are still widely used in development (CommonJS)

ES6's class is an object-oriented syntax sugar. It upgrades the writing method of prototype chain inheritance of ES5's constructor and does not solve the problem of modularization. Module function is proposed to solve this problem.

Before ES6, the community developed some module loading schemes, including CommonJS and AMD. CommonJS is used for server and AMD is used for browser.

The design idea of ES6 module is to be static as much as possible, so that the dependencies of the module and the input and output variables can be determined at compile time.

ES6 module is not an object, but the output code is explicitly specified through the export command, and the input is also in the form of static commands.

What is a module?

A module is a file. A script is a module. That's it.

Modules can be loaded with each other, and special instructions "export" and "import" can be used to exchange functions. Functions of one module can be called from another module:

  • The export keyword marks variables and functions that can be accessed from outside the current module.
  • The import keyword allows you to import functions from other modules.
import { Fun1, Fun2, Fun3 } from 'file';

The essence of the above code is to load three methods Fun1, Fun2 and Fun3 from the file module, and other methods are not loaded. This loading method is called compile time loading, that is, ES6 can complete module compilation at compile time.

A module is a separate file. All variables inside the file cannot be obtained externally. If you want to externally obtain a variable or method inside the module, you must use the export keyword to output the variable.

// profile.js
export var firstName = "John";
export var lastName = "Jackson";
export var year = 1999;

The above code is represented in profile JS file (module), three variables are output.

This expression is equivalent to:

var firstName = "John";
var lastName = "Jackson";
var year = 1999;
export { firstName, lastName, year };

Using braces after the export command specifies a set of variables to be output, which is equivalent to adding export before each variable.

In addition to outputting variables, the export command can also output functions and classes in the same way.

The output variable of export is the original name, but it can be renamed with the as keyword.

function f1() {}
function f2() {}
export { v1 as Fun1, v2 as Fun2, v2 as Foo };

You can use the as keyword to rename the same variable or method twice, so that it can be referenced with different names in the introduced module.

The export command can appear in any top-level scope of the module, not in the block level scope.

The value output by the export statement is dynamically bound to its module.

import command

After the external interface of the module is defined with the export command, other JS files can load the interface of the module through the import command.

// main.js
import { firstName, lastName, year } from './profile';

The import command accepts an object that specifies the variable names to be imported from other modules. The variable name in the object must be consistent with the interface variable name exported by the module to be loaded (if the interface does not use the as keyword, use the original variable name; if the as keyword is used, use the renamed interface name).

Similarly, if you want to rename the imported variable name, you can use the as keyword in the import command to rename the input variable.

// main.js
import { firstName as surname } from './profile';

In the above writing method, you need to write the variable name of each interface. If you want to load the whole module as a whole, you can use the asterisk (*) to specify an object to load all interfaces of the output module onto this object.

// main.js
import * as person from './profile';

The import command has the effect of promotion. It will be promoted to the top of the whole module and executed first.

export default command

The import command needs to know the interface name of the exported module in advance when loading the variable name or function name, otherwise it cannot be loaded. You can use the export default command to specify the default output interface of the module.

// profile.js
export default function () {
    console.log("my name is John Jackson, I was born in 1999");
}

In the above code, the profile module outputs a function by default. In this way, the import module does not need to specify the interface name to be loaded.

// main.js
import myName from './profile';
myName(); // "my name is John Jackson, I was born in 1999"

In main In the. JS file, myName refers to the default interface for profile file output. This means that the import command can point to the default interface of profile file output with any name without knowing the interface name.

The export default command can also be used in front of non anonymous functions. At this time, the function name is invalid outside the module and is regarded as an anonymous function during loading.

// profile.js
export default function sayName () {
    console.log("my name is John Jackson, I was born in 1999");
}

// main.js
import myName from './profile';
myName(); // "my name is John Jackson, I was born in 1999"

A module can only have one default output, so export default can only be used once in a module. Therefore, the corresponding import command may not be bracketed.

If you want to introduce both the default method and other variables into an import command, you can write it as follows:

import customName, { otherMethod } from './module-name';

customName refers to the name of the default interface, and otherMethod refers to other interfaces.

Module inheritance

Modules can inherit. Suppose a Circle module inherits the Shape module.

// cicle.js
export * from 'Shape';
export var pi = 3.14159265359;
export default function area(r) {
    return pi * r * r;
}

export * indicates all attributes and methods of the output module Shape, but does not output the default method of the Shape.

module command

If you want to load the module as a whole, you can use the module command instead of the above import * as command. The module command does not load the default method of the module. You need to use the Import command additionally to load the default method of the module.

// main.js
module person from './profile';

Essence of ES6 module loading

The ES6 module outputs a reference to a value.

When the ES6 module encounters the module loading command import, it will not execute the module, but only generate a dynamic read-only reference. Wait until it really needs to be used, and then take values in the module.

The input of ES6 is a bit like the "symbolic link" of UNIX system. When the original value changes, the input value will also change. Therefore, the ES6 module is dynamically referenced and does not cache values. Variables in the module are bound to the module in which they are located.

// lib.js
export let count = 3;
export function add() { count++; }

// main.js
import { count, add } from './lib';
console.log(count); // 3
add();
console.log(count); // 4

Since the module variable entered by ES6 is only a "symbolic link", this variable is read-only. If it is re assigned, a TypeError exception will be reported.

// lib.js
export let obj = {};

// main.js
import { obj } from './lib';
obj.prop = 123; // OK
obj = {}; // TypeError

The address pointed to by obj is read-only and cannot be reassigned.

Export of module interface

 

Import of module interface