1. Overview
Historically, JavaScript has never had a module system to split a large program into interdependent small files, and then assemble them in simple ways. Other languages have this feature, such as Ruby require, Python import, and even CSS have @import, but JavaScript does not support any of this, which is a huge obstacle to developing large, complex projects.
Before the ES6, the community developed a number of module loading programs, the main COMMONJS and AMD two kinds. The former is used for servers and the latter for browsers. ES6 at the level of language standards, the implementation of the module function, and the implementation of a fairly simple, completely replace the COMMONJS and AMD specifications, browser and server as a common modular solution.
The design idea of the ES6 module is to be as static as possible, so that the dependencies of the module can be determined at compile time, as well as the input and output variables. Commonjs and AMD modules can only be identified at runtime. For example, a COMMONJS module is an object that must look up object properties when entered.
ES6 can complete the module loading at compile time, the efficiency is higher than COMMONJS module loading mode. Of course, this also leads to the inability to refer to the ES6 module itself, because it is not an object.
Static analysis is possible because the ES6 module is loaded at compile time. With it, you can further broaden the syntax of JavaScript, such as introducing macros (macro) and type validation (types system), which can only be implemented by static analysis.
In addition to the various benefits of static loading, the ES6 module offers the following benefits.
The UMD module format is no longer required, and future servers and browsers will support the ES6 module format. At present, through a variety of tool libraries, has actually done this.
The new API for the future browser can be provided in module format and no longer has to be made into global variables or properties of Navigator objects.
Objects are no longer needed as namespaces (such as the Math object), and future functions can be provided through modules. 2.export Command
The function of module is mainly composed of two commands: Export and import. The Export command is used to specify the external interface of the module, and the Import command is used to enter the functionality provided by other modules.
A module is a separate file. All variables inside the file cannot be obtained externally. If you want to be able to read a variable inside a module externally, you must export the variable using the Export keyword. Here is a JS file, which uses the export command to output variables.
Profile.js
export var firstName = ' Michael ';
Export var lastName = ' Jackson ';
Export var year = 1958;
The code above is a profile.js file that holds the user information. ES6 it as a module in which three variables are exported externally with the Export command.
In addition to the above, there is another kind of export.
Profile.js
var firstName = ' Michael ';
var lastName = ' Jackson ';
var year = 1958;
Export {firstName, lastName, year};
The above code uses curly braces to specify a set of variables to output after the export command. It is equivalent to the previous notation (placed directly before the Var statement), but it should be given precedence. Because of this you can at the end of the script, at a glance clearly output which variables.
The export command can also output a function or Class (class), in addition to the output variables.
Export function multiply (x, y) {return
x * y;
};
The above code exports a function multiply.
Normally, the variable of export output is the original name, but can be renamed using the AS keyword.
Function v1 () {...}
Function v2 () {...}
Export {
v1 as streamV1,
v2 as streamV2,
v2 as Streamlatestversion
};
The code above uses the AS keyword to rename the external interface of the function V1 and v2. After renaming, V2 can output two times with a different name.
It is important to note that the Export command requires an external interface, which must establish a one by one correspondence with the variables inside the module.
Error
export 1;
Error
var m = 1;
Export m;
The above two types of writing will be an error, because the external interface is not provided. The first one is written directly out of 1, the second is written by the variable m, or by the direct output 1. 1 is only a value, not an interface. The correct wording is the following.
Writing one
export var m = 1;
Writing two
var m = 1;
Export {m};
Writing three
var n = 1;
Export {n as M};
The above three kinds of writing are correct, stipulate the external interface m. Other scripts can take the value 1 through this interface. The essence of them is that there are one by one corresponding relationships between the interface name and the internal variables of the module.
Similarly, the output of function and class must also be followed.
Error
function f () {}
export F;
Correct
export function f () {};
Correct
function f () {}
export {f};
In addition, the Export statement output interface, and its corresponding value is a dynamic binding relationship, that is, through the interface, you can take the real-time value inside the module.
Export var foo = ' Bar ';
SetTimeout (() => foo = ' Baz ', 500);
The above code output variable foo, the value is bar,500 milliseconds and then becomes Baz.
This is completely different from the COMMONJS specification. COMMONJS module output is the value of the cache, there is no dynamic update, as described in the following "Module load implementation" section.
Finally, the Export command can appear anywhere in the module, as long as it is at the top level of the module. If you are in a block-level scope, the error occurs, as is the import command in the next section. This is because in the condition code block, cannot do static optimization, violates the ES6 module design original intention.
function foo () {
export default ' bar '//SyntaxError
}
foo ()
In the code above, the export statement is placed in the function and the result is an error. 3.import Command
After using the export command to define the external interface of the module, the other JS files can load the module through the import command.
Main.js
Import {firstName, lastName, year} from './profile ';
function SetName (Element) {
element.textcontent = firstName + ' + lastName;
}
The import command for the above code to load the Profile.js file and enter variables from it. The import command accepts a pair of curly braces that specify the name of the variable to import from another module. The variable name inside the curly braces must be the same as the name of the external interface of the imported module (profile.js).
If you want to rename the variable you entered, the import command uses the AS keyword to rename the variable you entered.
Import {LastName as surname} from './profile ';
The from specifies the location of the module file after import, either relative or absolute, and the. js suffix can be omitted. If it is only a module name and does not have a path, then there must be a configuration file that tells the JavaScript engine where the module is located.
Import {MyMethod} from ' Util ';
In the code above, util is the module file name, because without the path, must be configured to tell the engine how to access this module.
Note that the import command has a lifting effect that is elevated to the head of the entire module, first executed.
Foo ();
Import {foo} from ' My_module ';
The above code does not complain because the import was performed earlier than the call to Foo. The essence of this behavior is that the import command is executed at compile time, before the code runs.
Because import is static execution, expressions and variables cannot be used, which can only get the syntax structure of the result at run time.
Error
Import {' F ' + ' oo '} from ' My_module ';
Error let
module = ' My_module ';
Import {foo} from module;
Error
if (x = = 1) {
import {foo} from ' Module1 ';
} else {
import {foo} from ' Module2 ';
}
The above three kinds of writing will be the error, because they use the expression, variable and if structure. In the static analysis phase, these grammars are not able to get the value.
Finally, the import statement executes the loaded module, so you can have the following wording.
Import ' Lodash ';
The above code only executes the Lodash module, but does not enter any values.
If the same import statement is repeatedly executed repeatedly, it is executed only once, not multiple times.
Import ' Lodash ';
Import ' Lodash ';
The code above is loaded two times Lodash, but it is only performed once.
Import {foo} from ' My_module ';
Import {bar} from ' My_module ';
Equivalent to
import {foo, bar} from ' My_module ';
In the code above, although Foo and bar are loaded in two statements, they correspond to the same my_module instance. In other words, the import statement is Singleton mode.
At this stage, through the Babel transcoding, the Commonjs module's require command and the ES6 module's import command, can be written in the same module, but it is best not to do so. Because import executes in the static parsing phase, it is the first of a module to execute. The following code may not get the expected results.
Require (' Core-js/modules/es6.symbol ');
Require (' core-js/modules/es6.promise ');
Import react from ' react ';
4. The overall load of the module
In addition to specifying that an output value is loaded, you can also use the overall load, which is to specify an object with an asterisk (*), and all output values are loaded on the object.
Below is a circle.js file that outputs two method area and circumference.
Circle.js
Export function Area (RADIUS) {return
Math.PI * radius * RADIUS;
}
Export function circumference (RADIUS) {return
2 * math.pi * RADIUS;
}
Now, load this module.
Main.js
Import {area, circumference} from './circle ';
Console.log (' Round area: ' + areas (4));
Console.log (' circumference length: ' + circumference (14));
The above writing is to specify the method to be loaded, the overall load is written as follows.
Import * as Circle from './circle ';
Console.log (' Round area: ' + Circle.area (4));
Console.log (' circumference length: ' + circle.circumference (14));
Note that the object in which the module is loaded as a whole (the example above is circle) should be statically parsed, so runtime changes are not allowed. The following wording is not allowed.
Import * as Circle from './circle ';
The following two lines are not allowed
Circle.foo = ' Hello ';
Circle.area = function () {};
5.export default Command
As can be seen from the previous example, when using the import command, the user needs to know the name of the variable or function to be loaded, otherwise it cannot be loaded. However, users will certainly want to get started quickly and may not be willing to read the documentation to see what properties and methods the module has.
In order to provide convenience to users, so that they can load the module without reading the document, the Export default command is used to specify the default output for the module.
Export-default.js
Export Default function () {
console.log (' foo ');
}
The code above is a module file Export-default.js, and its default output is a function.
When another module loads the module, the import command can specify an arbitrary name for the anonymous function.
Import-default.js
import Customname from './export-default ';
Customname (); ' Foo '
The import command for the above code, which can point to the export-default.js output with any name, does not need to know the function name of the original module output. Note that, after the import command, no curly braces are used.
The Export default command is also available before it is used in a non-anonymous function.
Export-default.js
Export default function foo () {
console.log (' foo ');
}
or write
function foo () {
console.log (' foo ');
}
Export default foo;
In the code above, the function name Foo of the Foo function is not valid outside the module. When loaded, the same anonymous function is loaded.
The default output and normal output are compared below.
The first group of
export default function Crc32 () {//output
//...
}
Import CRC32 from ' CRC32 '; Input
//second group
export function Crc32 () {//output
//...
};
Import {CRC32} from ' CRC32 '; Input
The two sets of code above, the first group is the use of export default, the corresponding import statement does not need to use curly braces, the second group is not using export default, the corresponding import statement requires the use of curly braces.
The Export default command is used to specify the default output for a module. Obviously, a module can have only one default output, so the export default command can only be used once. Therefore, the import command does not need to be enlarged after parentheses because only the export default command is possible.
In essence, export default is to output a variable or method called default, and the system allows you to name it arbitrarily. Therefore, the following wording is valid.
Modules.js
function Add (x, y) {return
x * y;
}
Export {add as default};
Equivalent to
//export default add;
App.js
Import {default as Foo} from ' modules ';
Equivalent to
//import foo from ' modules ';
It is because the export default command simply outputs a variable called default, so it cannot be followed by a variable declaration statement.
Correct
export var a = 1;
Correct
var a = 1;
Export default A;
Error
Export default var a = 1;
In the code above, the meaning of export default A is to assign the value of variable A to the variable default. Therefore, the last type of writing will be an error.
Similarly, because the essence of the Export default command is to assign the following value to the default variable, you can write a value directly after export default.
correct
export default;
Error
export 42;
the compound writing of 6.export and import
If in a module, first input and then output the same module, the import statement can be written with the export statement.
Export {foo, bar} from ' My_module ';
Equivalent to
import {foo, bar} from ' My_module ';
Export {foo, bar};
In the code above, the export and import statements can be combined and written in one line.
The interface renaming and overall output of the module can also be used.
Interface renamed
Export {foo as Myfoo} from ' My_module ';
Total Export Export
* from ' my_module ';
The default interface is worded as follows.
Export {default} from ' Foo ';
The named interface is changed to the default interface as follows.
Export {ES6 as default} from './somemodule ';
Equivalent to
import {ES6} from './somemodule ';
Export default ES6;
Similarly, the default interface can be renamed as a named interface.
Export {default as ES6} from './somemodule ';
The following three import statements, there is no corresponding composite writing.
Import * as Someidentifier from "Somemodule";
Import Someidentifier from "Somemodule";
Import Someidentifier, {namedidentifier} from "Somemodule";
In order to achieve the form of symmetry, there are now proposals to make up the three kinds of compound writing.
Export * as Someidentifier from "Somemodule";
Export someidentifier from "Somemodule";
Export Someidentifier, {namedidentifier} from "Somemodule";
7. Module Inheritance
can also be inherited between modules.
Suppose you have a Circleplus module that inherits the Circle module.
Circleplus.js
Export * from ' circle ';
Export var e = 2.71828182846;
Export default function (x) {return
math.exp (x);
}
The export in the above code represents all the properties and methods of the Circle module. Note that the export command ignores the default method of the Circle module. The code then prints the custom e variable and the default method.
At this point, you can also circle the properties or methods, renamed and then output.
Circleplus.js
Export {area as Circlearea} from ' Circle ';
The code above indicates that only the area method of the Circle module is exported and renamed to Circlearea.
The above module is loaded with the following wording.
Main.js
Import * as math from ' circleplus ';
Import exp from ' Circleplus ';
Console.log (exp (MATH.E));
The import exp in the code above indicates that the default method of the Circleplus module is loaded as an exp method. 8. Cross-module constants
When this book introduces the const command, it says that Const declared constants are valid only in the current block of code. If you want to set constants across modules (that is, across multiple files), or if a value is shared by multiple modules, you can use the following notation.
Constants.js Module
Export const A = 1;
Export const B = 3;
Export const C = 4;
Test1.js Module
Import * as constants from './constants ';
Console.log (constants. A); 1
Console.log (constants. B); 3
//test2.js module
Import {A, B} from './constants ';
Console.log (A); 1
console.log (B);//3
If you want to use a lot of constants, you can build a special constants directory, the various constants written in different files, stored in the directory.
Constants/db.js
Export Const DB = {
URL: ' http://my.couchdbserver.local:5984 ',
admin_username: ' admin ' ,
admin_password: ' admin password '
};
Constants/user.js
Export Const users = [' root ', ' admin ', ' staff ', ' CEO ', ' chief ', ' moderator '];
Then, the constants that are output from these files are merged into the index.js.
Constants/index.js
Export {db} from './db ';
Export {users} from './users ';
When used, the direct loading of the index.js can be.
Script.js
Import {db, users} from './index ';