Module syntax
1 Introduction
Before ES6, there were mainly two kinds of CommonJS
and AMD
. CommonJS for the server, AMD for the browser. ES6 implements module functions at the level of language standards, and the implementation is quite simple. It can completely replace CommonJS and AMD specifications and become a common module solution for browsers and servers. Entered the grand unification stage.
The design idea of ES6 modules is to be as static as possible, makingcompile timeYou can determine the dependencies of the module, as well as the input and output variables. Both CommonJS and AMD modules can only be used inRuntimeMake sure of these things.
// CommonJS模块
let {
stat, exists, readfile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
This kind of loading is called "runtime loading", because this object can only be obtained at runtime, resulting in no way to do "static optimization" at compile time.
export
ES6 modules are not objects, but codes that explicitly specify output through commands, and then import
input through commands.
// ES6模块
import {
stat, exists, readFile } from 'fs';
The essence of the above code is to load 3 methods from the fs module, and other methods are not loaded. This loading is called "compile-time loading" or static loading. That is, ES6 can complete module loading at compile time, which is more efficient than CommonJS module loading. Of course, this also makes it impossible to reference the ES6 module itself, since it's not an object.
2. Strict mode
ES6 modules automatically adopt strict mode, whether you add it to the module header or not "use strict"
.
Strict mode mainly has the following restrictions.
- Variables must be declared before they can be used
- The parameters of the function cannot have attributes with the same name, otherwise an error will be reported
- cannot use the with statement
- You cannot assign a value to a read-only attribute, otherwise an error will be reported
- Undeletable attributes cannot be deleted, otherwise an error will be reported
- Variables cannot be deleted
delete prop
, an error will be reported, only attributes can be deleteddelete global[prop]
eval
Does not introduce variables in its outer scopeeval
andarguments
cannot be reassignedarguments
Does not automatically reflect changes in function parameters- Out of service
arguments.callee
- Out of service
arguments.caller
- Do not
this
point to the global object - Can't use
fn.caller
andfn.arguments
get the stack of a function call - Added reserved words (such as
protected
,static
andinterface
)
3. export command
The module function mainly consists of two commands: export
and import
. export
The command is used to specify the external interface of the module, and import
the command is used to input the functions provided by other modules.
A module is a single file. All variables inside the file cannot be obtained from the outside. If you want the outside to be able to read a variable inside the module, you must use export
the keyword to output the variable. Below is a JS file that uses export
command output variables.
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
// 另一种写法
// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {
firstName, lastName, year };
export
In addition to outputting variables, commands can also output functions or classes.
export function multiply(x, y) {
return x * y;
};
The above code outputs a function externally multiply
.
Normally, export
the output variable is the original name, but can be as
renamed using keywords.
function v1() {
... }
function v2() {
... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
What needs special attention is that export
the command specifies the external interface, which must establish a one-to-one correspondence with the variables inside the module.
// 报错
export 1;
// 报错
var m = 1;
export m;
The above two ways of writing will report an error, because no external interface is provided. The first way of writing is directly output 1
, and the second way of writing m
is output directly through variables 1
. 1
Just a value, not an interface. The correct way of writing is as follows.
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {
m};
// 写法三
var n = 1;
export {
n as m};
The above three writing methods are all correct, specifying the external interface m. Other scripts can get the value 1 through this interface. Their essence is to establish a one-to-one correspondence between the interface name and the internal variables of the module.
Similarly, the output of function
and class
must also follow this way of writing.
// 报错
function f() {
}
export f;
// 正确
export function f() {
};
// 正确
function f() {
}
export {
f};
4. import command
After using export
the command to define the external interface of the module, other JS files can import
load this module through the command.
// main.js
import {
firstName, lastName, year } from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
If you want to rename the input variable, import
the command must use as
keywords to rename the input variable.
import {
lastName as surname } from './profile.js';
import
The variables entered by the command are all read-only, because its essence is an input interface. In other words, it is not allowed to rewrite the interface in the script that loads the module.
import {
a} from './xxx.js'
a = {
}; // Syntax Error : 'a' is read-only;
In the above code, the script loads the variable a
, and if it is reassigned, an error will be reported, because a is a read-only interface. However, if a
it is an object, overriding a
properties are allowed.
import {
a} from './xxx.js'
a.foo = 'hello'; // 合法操作
5. Overall loading of modules
In addition to specifying to load a certain output value, you can also use overall loading, that is, use an asterisk (*) to specify an object, and all output values are loaded on this object.
Below is a circle.js file that exports two methods 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('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));
The above method is to specify the methods to be loaded one by one, and the overall loading method is as follows.
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
Note that the object where the module is loaded as a whole (circle in the above example) should be statically analyzable, so runtime changes are not allowed. The following writing is not allowed.
import * as circle from './circle';
// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {
};
6. export default command
When using import
the command, the user needs to know the name of the variable or function to be loaded, otherwise it cannot be loaded. However, users definitely want to get started quickly, and may not be willing to read the documentation. So for convenience, the command is used to export default
specify the default output for the module.
// export-default.js
export default function () {
console.log('foo');
}
The above code is a module file export-default.js
, and its default output is a function.
When other modules load this module, import
the command can specify any name for the anonymous function.
// import-default.js
import customName from './export-default';
customName(); // 'foo'
The command of the above code import
can use any name to point to export-default.js
the output method. At this time, there is no need to know the function name output by the original module. It should be noted that import
curly braces are not used after the command at this time.
export default
It is also possible to use commands before non-anonymous functions.
// export-default.js
export default function foo() {
console.log('foo');
}
// 或者写成
function foo() {
console.log('foo');
}
export default foo;
In the above code, foo
the function name of the function foo
is invalid outside the module. When loading, it is regarded as an anonymous function loading.
// 第一组
export default function crc32() {
// 输出
// ...
}
import crc32 from 'crc32'; // 输入
// 第二组
export function crc32() {
// 输出
// ...
};
import {
crc32} from 'crc32'; // 输入
The above two groups of codes are written, the first group is used export default
, the corresponding import
statement does not need to use braces; the second group is not used export default
, the corresponding import
statement needs to use braces.
The export default command is used to specify the default output of the module. Obviously, a module can only have one default output, so the export default command can only be used once. Therefore, there is no need to use braces after the import command, because it can only correspond to the export default command.
In essence, export default
it is to export a default
variable or method called , and then the system allows you to give it any name. Therefore, the following writing is valid.
// modules.js
function add(x, y) {
return x * y;
}
export {
add as default};
// 等同于
// export default add;
// app.js
import {
default as foo } from 'modules';
// 等同于
// import foo from 'modules';
7. Compound writing of export and import
If in a module, the same module is input first and then output, import
the statement can export
be written together with the statement.
export {
foo, bar } from 'my_module';
// 可以简单理解为
import {
foo, bar } from 'my_module';
export {
foo, bar };
In the above code, the export
AND import
statement can be combined and written into one line. However, it should be noted that after being written in one line, foo
and bar
is not actually imported into the current module, but is equivalent to forwarding these two interfaces externally, so that the current module cannot directly use foo
and bar
.
The interface renaming and overall output of the module can also be written in this way.
// 接口改名
export {
foo as myFoo } from 'my_module';
// 整体输出
export * from 'my_module';
The default interface is written as follows.
export {
default } from 'foo';
The writing method of changing the named interface to the default interface is as follows.
export {
es6 as default } from './someModule';
// 等同于
import {
es6 } from './someModule';
export default es6;
Likewise, the default interface can also be renamed to a named interface.
export {
default as es6 } from './someModule';
Before ES2020, there was an import statement, but there was no corresponding compound writing method.
import * as someIdentifier from "someModule";
ES2020 added this way of writing.
export * as ns from "mod";
// 等同于
import * as ns from "mod";
export {
ns};