Module Syntax

<>Module Syntax

<> Summary

Historically,JavaScript There has been no module(module) system, Cannot split a large program into interdependent small files, Put them together in a simple way. It's available in other languages, such as
Ruby Ofrequire,Python Ofimport, Even even CSS Both [email protected], however JavaScript
There's no support for that, It's important to develop, Complex projects create huge obstacles.

stay ES6 before, The community has developed some module loading schemes, The main ones are CommonJS and AMD two types. Former for server, The latter is used for browsers.ES6
On the level of language standard, Module function is realized, And it's quite simple, Completely replaceable CommonJS and AMD Standard, Become a common module solution for browser and server.

ES6 Design idea of module, It's as static as possible, Enables module dependencies to be determined at compile time, And input and output variables.CommonJS and AMD
Modular, You can only identify these things at run time. such as,CommonJS Modules are objects, Object properties must be found on import.
// CommonJS Modular let { stat, exists, readFile } = require('fs'); // Equate to let _fs =
require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile =
The essence of the above code is overall loadingfs Modular( That is loadingfs All methods of), Generate an object(_fs), Then read from the object 3
One way. This load is called“ Load at run time”, Because only the runtime can get this object, So there's no way to do it at compile time“ Static optimization”.

ES6 Module is not an object, But throughexport Command explicitly specifies the output code, Re passimport Command input.
// ES6 Modular import { stat, exists, readFile } from 'fs';
The essence of the above code is fromfs Module loading 3 One way, Other methods do not load. This load is called“ Load at compile time” Or static load, Namely ES6 Module loading can be done at compile time, Efficiency ratio
CommonJS High loading mode of module. Of course, It also makes it impossible to quote ES6 Module itself, Because it's not an object.

Because ES6 Modules are loaded at compile time, Make static analysis possible. With it, Can be further broadened JavaScript Syntax, For example, introduce macro(macro) And type inspection(type
system) These functions can only be realized by static analysis.

In addition to the benefits of static loading,ES6 The module also has the following benefits.

* No longer neededUMD Module format, In the future, both server and browser will support ES6 Module format. at present, Through various tool Libraries, In fact, this has been done.
* New browser in the future API Can be provided in module format, It no longer has to be a global variable ornavigator Object properties.
* Object is no longer required as a namespace( such asMath object), In the future, these functions can be provided through modules.
This chapter is an introduction. ES6 Module syntax, The next chapter describes how to use the Node In, Load ES6 Modular.

<> Strict mode

ES6 Strict mode is adopted automatically in the module of, Whether or not you add"use strict";.

The strict model mainly has the following limitations.

* Variables must be declared before they can be used
* A parameter of a function cannot have a property with the same name, Otherwise, report wrong.
* Out of commissionwith Sentence
* Cannot assign to read-only property, Otherwise, report wrong.
* Cannot use prefix 0 Represents octal number, Otherwise, report wrong.
* Properties that cannot be deleted cannot be deleted, Otherwise, report wrong.
* Cannot delete variabledelete prop, Report wrong, Only attributes can be deleteddelete global[prop]
* eval Does not introduce variables in its outer scope
* eval andarguments Cannot be reassigned
* arguments Does not automatically reflect changes in function parameters
* Out of commissionarguments.callee
* Out of commissionarguments.caller
* prohibitthis Point to global object
* Out of commissionfn.caller andfn.arguments Get the stack of function calls
* Reserved words added( such asprotected,static andinterface)
These restrictions, Modules must comply. Because the strict pattern is ES5 Introduced, Not belong to ES6, So please refer to the relevant ES5 book, This book is no longer detailed.

among, Special attentionthis Restrictions.ES6 Module, top-levelthis pointundefined, That is, it should not be used in the top-level codethis.

<>export command

Module function is mainly composed of two commands:export andimport.export Command is used to specify the external interface of the module,import Commands are used to enter functions provided by other modules.

A module is a separate file. All variables within the file, External cannot get. If you want the external to be able to read a variable inside the module, Must useexport Keyword output the variable. Here is a
JS file, Use insideexport Command output variable.
// profile.js export var firstName = 'Michael'; export var lastName = 'Jackson'
;export var year = 1958;
The code above isprofile.js file, Saved user information.ES6 Think of it as a module, Inside useexport The command outputs three variables externally.

export Writing, Except like above, There's another one.
// profile.js var firstName = 'Michael'; var lastName = 'Jackson'; var year =
1958; export {firstName, lastName, year};
The above code is inexport Command behind, Use braces to specify a set of variables to output. It and the former( Directly placed invar
Statement before) It is equivalent. But we should give priority to this writing method. Because it's at the end of the script, See what variables are output at a glance.

export Commands except output variables, You can also output functions or classes(class).
export function multiply(x, y) { return x * y; };
The above code outputs a functionmultiply.

under normal conditions,export The output variable is the original name, But you can useas Keyword rename.
function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as
streamV2, v2as streamLatestVersion };
Use of the above codeas Keyword, Renamed functionv1 andv2 External interface of. After renaming,v2 It can be output twice with different names.

What needs special attention is,export The order specifies an external interface, One to one correspondence must be established with the variables inside the module.
// Report errors export 1; // Report errors var m = 1; export m;
Both of the above methods will report errors, Because no external interface is provided. The first writing method is direct output 1, The second way is through variablesm, Or direct output 1.1 It's just a value, Not interface. The correct way to write it is as follows.
// Write a way export var m = 1; // Writing two var m = 1; export {m}; // Writing three var n = 1; export
{nas m};
All three of the above are correct, Specified external interfacem. Other scripts can use this interface, Fetch value1. Their essence is, Between interface name and module internal variables, A one-to-one correspondence is established.

Alike,function andclass Output, It must also be written in this way.
// Report errors function f() {} export f; // Correct export function f() {}; // Correct function f(
){} export {f};
in addition,export Interface for statement output, The corresponding value is a dynamic binding relationship, Through this interface, It can get the real-time value inside the module.
export var foo = 'bar'; setTimeout(() => foo = 'baz', 500);
Output variable of the above codefoo, The value isbar,500 Milliseconds laterbaz.

This point and CommonJS Specifications are totally different.CommonJS The module outputs a cache of values, There is no dynamic update, See below.《Module Load implementation of》 Section.

Last,export Commands can appear anywhere in the module, As long as it is at the top of the module. If in block level scope, You will report wrong. Next sectionimport
So is the order. This is because it is in a conditional block of code, You can't do static optimization, Contrary to ES6 Original intention of module design.
function foo() { export default 'bar' // SyntaxError } foo()
In the above code,export Statement in function, Result reported wrong.

<>import command

Useexport After the command defines the external interface of the module, Other JS The file can be passedimport Command to load this module.
// main.js import {firstName, lastName, year} from './profile'; function
setName(element) { element.textContent = firstName + ' ' + lastName; }
Code aboveimport command, Used for loadingprofile.js file, And enter variables from it.import
Command accepts a pair of braces, It specifies the variable name to be imported from other modules. Variable names in braces, Must be associated with the imported module(profile.js) The name of the external interface is the same.

If you want to rename the input variable,import Command to useas Keyword, Rename the input variable.
import { lastName as surname } from './profile';
import Hinderfrom Specify the location of the module file, Can be a relative path, It can also be an absolute path,.js Suffixes can be omitted. If it's just the module name, Without path, Then there must be a profile, tell
JavaScript Engine location of the module.
import {myMethod} from 'util';
In the above code,util Is the module filename, Because there is no path, Must be configured by, Tell the engine how to get this module.

Be careful,import Command has promotion effect, It will be lifted to the head of the whole module, First execution.
foo(); import { foo } from 'my_module';
The above code will not report an error, becauseimport Execution of is earlier thanfoo Call. The essence of this behavior is,import Commands are executed during the compile phase, Before the code runs.

Becauseimport Is static execution, So you can't use expressions and variables, These syntax structures are only available at run time.
// Report errors import { 'f' + 'oo' } from 'my_module'; // Report errors let module = 'my_module';
import { foo } from module; // Report errors if (x === 1) { import { foo } from 'module1';
}else { import { foo } from 'module2'; }
All of the above three writing methods will report errors, Because they use expressions, Variable sumif structure. In the static analysis stage, These grammars don't get value.

Last,import Statement executes the loaded module, Therefore, it can be written as follows.
import 'lodash';
The above code only executeslodash Modular, But no value is entered.

If you repeat the same sentence multiple timesimport Sentence, Then only once, Not more than once.
import 'lodash'; import 'lodash';
The above code has been loaded twicelodash, But only once.
import { foo } from 'my_module'; import { bar } from 'my_module'; // Equate to import
{ foo, bar }from 'my_module';
In the above code, althoughfoo andbar Load in two statements, But they are the samemy_module Example. In other words,import Statement is Singleton Pattern.

Current stage, adopt Babel transcoding,CommonJS Modularrequire Command and ES6 Modularimport
command, Can be written in the same module, But it's better not to. becauseimport Execute in static parsing phase, So it's the first module to execute. The following code may not get the expected result.
require('core-js/modules/es6.symbol'); require('core-js/modules/es6.promise');
import React from 'React';
<> Overall loading of modules

In addition to specifying to load an output value, You can also use bulk loading, Asterisk(*) Specify an object, All output values are loaded on this object.

Here is acircle.js file, It outputs two methodsarea andcircumference.
// 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:'
+ area(4)); console.log(' Circumference of circle:' + circumference(14));
The above method is to specify the method to be loaded one by one, The whole load is written as follows.
import * as circle from './circle'; console.log(' Round area:' + circle.area(4));
console.log(' Circumference of circle:' + circle.circumference(14));
Be careful, The object where the module is loaded as a whole( The above example iscircle), It should be able to analyze statically, So runtime changes are not allowed. The following are not allowed.
import * as circle from './circle'; // The following two lines are not allowed = 'hello';
circle.area =function () {};
<>export default command

As can be seen from the previous example, Useimport
When ordered, The user needs to know the variable name or function name to load, Otherwise, it cannot be loaded. however, Users definitely want to get started quickly, Not necessarily willing to read documents, To understand the attributes and methods of modules.

In order to provide convenience for users, Let them load the module without reading the document, It's going to be used.export default command, Specify the default output for the module.
// export-default.js export default function () { console.log('foo'); }
The above code is a module fileexport-default.js, Its default output is a function.

When other modules load the module,import Command can specify any name for the anonymous function.
// import-default.js import customName from './export-default'; customName();
// 'foo'
Code aboveimport command, Can point to with any nameexport-default.js Output method, In this case, you do not need to know the function name output by the original module. It should be noted that, Thenimport
Command behind, Do not use braces.

export default Command before non anonymous function, It's ok.
// export-default.js export default function foo() { console.log('foo'); } //
Or write it function foo() { console.log('foo'); } export default foo;
In the above code,foo Function name of functionfoo, Invalid outside module. When loading, Load as anonymous function.

Let's compare the default output with the normal output.
// first group export default function crc32() { // output // ... } import crc32 from
'crc32'; // input // Second group export function crc32() { // output // ... }; import {crc32}
from 'crc32'; // input
Two writing methods of the above code, The first group is usingexport default Time, Correspondingimport Statement does not need braces; The second group is not to useexport default Time, Corresponding
import Statement requires braces.

export default Command to specify the default output of the module. Obviously, A module can only have one default output, thereforeexport default Command can only be used once. therefore,import
Do not use braces after commands, Because there is only one way.

In essence,export default Is to output adefault Variable or method of, And then the system allows you to name it anything. therefore, The following is valid.
// modules.js function add(x, y) { return x * y; } export {add as default}; //
Equate to // export default add; // app.js import { default as foo } from 'modules';
// Equate to // import foo from 'modules';
Precisely becauseexport default The command is just an output calleddefault Variables, 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 above code,export default a The meaning of variablea The value of is assigned to the variabledefault. therefore, The last way to write is to report mistakes.

Similarly, becauseexport default Essentially, the value after the command, Assign todefault Variable defaults later, So write a value directly inexport default after.
// Correct export default 42; // Report errors export 42;
In the above code, The next error is reported because no external interface is specified, In the previous sentence, the external interface is specified asdefault.

Yes.export default command, The input module is very intuitive, Input lodash Module as an example.
import _ from 'lodash';
If you want toimport Statement, Enter default methods and other interfaces at the same time, It can be written as follows.
import _, { each, each as forEach } from 'lodash';
Corresponding to the above codeexport Statements are as follows.
export default function (obj) { // ··· } export function each(obj, iterator,
context) { // ··· } export { each as forEach };
The last line of the above code means, Violent露出forEach接口,默认指向each接口,即forEach和each指向同一个方法.

export default也可以用来输出类.
// MyClass.js export default class { ... } // main.js import MyClass from
'MyClass'; let o = new MyClass();
<>export 与 import 的复合写法

export { foo, bar } from 'my_module'; // 等同于 import { foo, bar } from
'my_module'; export { foo, bar };

// 接口改名 export { foo as myFoo } from 'my_module'; // 整体输出 export * from
export { default } from 'foo';
export { es6 as default } from './someModule'; // 等同于 import { es6 } from
'./someModule'; export default es6;
export { default as es6 } from './someModule';
import * as someIdentifier from "someModule"; import someIdentifier from
"someModule"; import someIdentifier, { namedIdentifier } from "someModule";
为了做到形式的对称,现在有提案 <>
export * as someIdentifier from "someModule"; export someIdentifier from
"someModule"; export someIdentifier, { namedIdentifier } from "someModule";


// circleplus.js export * from 'circle'; export var e = 2.71828182846; export
default function(x) { return Math.exp(x); }
上面代码中的export *,表示再输出circle模块的所有属性和方法.注意,export *命令会忽略circle模块的default

// circleplus.js export { area as circleArea } from 'circle';

// main.js import * as math from 'circleplus'; import exp from 'circleplus';
上面代码中的import exp表示,将circleplus模块的默认方法加载为exp方法.


// constants.js 模块 export const A = 1; export const B = 3; export const C = 4;
// test1.js 模块 import * as constants from './constants'; console
.log(constants.A);// 1 console.log(constants.B); // 3 // test2.js 模块 import {A,
B}from './constants'; console.log(A); // 1 console.log(B); // 3
// 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'];
// constants/index.js export {db} from './db'; export {users} from './users';
// script.js import {db, users} from './index';


前面介绍过,import命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行(叫做”连接“更合适).所以,下面的代码会报错.
// 报错 if (x === 2) { import MyModual from './myModual'; }

这样的设计,固然有利于编译器提高效率,但也导致无法在运行时加载模块.在语法上,条件加载就不可能实现.如果import命令要取代 Node 的require
const path = './' + fileName; const myModual = require(path);

因此,有一个提案 <>,建议引入import()

import()返回一个 Promise 对象.下面是一个例子.
const main = document.querySelector('main'); import(`./section-modules/
${someVariable}.js`) .then(module => { module.loadPageInto(main); }) .catch(err
=> { main.textContent = err.message; });

import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载.




button.addEventListener('click', event => { import('./dialogBox.js') .then(
dialogBox => {; }) .catch(error => { /* Error handling */ })


if (condition) { import('moduleA').then(...); } else { import('moduleB'
).then(...); }
上面代码中,如果满足条件,就加载模块 A,否则加载模块 B.


import(f()) .then(...);


import('./myModule.js') .then(({export1, export2}) => { // ...· });

import('./myModule.js') .then(myModule => { console.log(myModule.default); });
import('./myModule.js') .then(({default: theDefault}) => { console
.log(theDefault); });
Promise.all([ import('./module1.js'), import('./module2.js'), import(
'./module3.js'), ]) .then(([module1, module2, module3]) => { ··· });
import()也可以用在 async 函数之中.
async function main() { const myModule = await import('./myModule.js'); const
{export1, export2} =await import('./myModule.js'); const [module1, module2,
module3] =await Promise.all([ import('./module1.js'), import('./module2.js'),
import('./module3.js'), ]); } main();