For more details, please go to: Ruan Yifeng – ES6-Module – well written.

An overview of the

Prior to ES6, there were several module loading schemes developed by the community, the most important being CommonJS and AMD. The former is used for servers and the latter for browsers. ES6 in the language standard level, the realization of module function, and the implementation is quite simple, can completely replace CommonJS and AMD specifications, become a common browser and server module solution.

The ES6 module is designed to be as static as possible, so that the module dependencies, as well as the input and output variables, can be determined at compile time. Both CommonJS and AMD modules can only determine these things at runtime. For example, a CommonJS module is an object, and you have to look for object properties when you enter it.

/ / CommonJS module
let { stat, exists, readfile } = require('fs');

/ / is equivalent to
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
Copy the code

The above code essentially loads the FS module as a whole (that is, all the methods that load FS), generates an object (_fs), and then reads three methods from that object. This type of loading is called “runtime loading” because the object is only available at runtime, making it completely impossible to do “static optimization” at compile time.

An ES6 module is not an object. Instead, it explicitly specifies the output code through the export command, which is then input through the import command.

/ / ES6 module
import { stat, exists, readFile } from 'fs';
Copy the code

The essence of the above code is to load three methods from the FS module and none of the others. This type of loading is called “compile-time loading” or static loading, meaning ES6 modules can be loaded at compile time, which is much more efficient than CommonJS modules. Of course, this also makes it impossible to reference the ES6 module itself because it is not an object.

Because ES6 modules are loaded at compile time, static analysis is possible. With it, you can further expand JavaScript syntax by introducing features such as macros and type systems that can only be implemented with static analysis.

Strict mode

ES6 modules automatically adopt strict mode, whether or not you add “Use strict” to the module header; .

Strict mode has the following limitations:

  • Variables must be declared before being used
  • Function arguments cannot have attributes of the same name; otherwise, an error is reported
  • You can’t usewithstatements
  • Cannot assign a value to a read-only attribute, otherwise an error is reported
  • Octal numbers cannot be represented with the prefix 0, otherwise an error is reported
  • You cannot delete attributes that cannot be deleted; otherwise, an error is reported
  • Cannot delete variablesdelete prop, an error is reported, and only attributes can be deleteddelete global[prop]
  • evalNo variables are introduced in its outer scope
  • evalandargumentsCannot be reassigned
  • argumentsDoes not automatically reflect changes in function parameters
  • You can’t usearguments.callee
  • You can’t usearguments.caller
  • banthisPointing to a global object
  • You can’t usefn.callerandfn.argumentsGets the stack of function calls
  • Added reserved words (e.gprotected,staticandinterface)

export

The function of the module consists of two commands: export and import. The export command is used to specify the external interface of a module, and the import command is used to input functions provided by other modules.

A module is an independent file. All variables inside the file are not available externally. If you want outsiders to be able to read a variable inside a module, you must use the export keyword to output that variable. Here is a JS file that uses the export command to output variables.

// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
Copy the code

The above code is the profile.js file, which saves the user information. ES6 treats it as a module that prints three variables to the outside world using the export command.

So there’s another way to write export.

// profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export { firstName, lastName, year };
Copy the code

The code above, following the export command, uses curly braces to specify the set of variables to output. It is equivalent to the former (placed directly before the VAR statement), but should be preferred. This way, at the end of the script, you can see what variables are being output.

The export command can output functions or classes as well as variables.

export function multiply(x, y) {
  return x * y;
};
Copy the code

The code above outputs a function called multiply.

Normally, the variable output by export 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
};
Copy the code

The above code renames the external interfaces of the functions v1 and v2 using the AS keyword. After the rename, V2 can be printed twice with different names.

It should be noted that the export command specifies the external interface and must establish a one-to-one correspondence with variables inside the module.

/ / an error
export 1;

/ / an error
var m = 1;
export m;
Copy the code

Both of the above methods give an error because there is no external interface provided. The first way to write it is to print 1 directly, and the second way to write it is to print m directly, again. 1 is just a value, not an interface. The correct way to write it is the following.

/ / write one
export var m = 1;

/ / write two
var m = 1;
export {m};

/ / writing three
var n = 1;
export {n as m};
Copy the code

The above three notations are correct, specifying the external interface M. Other scripts can use this interface to get the value 1. Their essence is to establish a one-to-one correspondence between the interface name and the module’s internal variables.

Similarly, the output of function and class must be written this way.

/ / an error
function f() {}
export f;

/ / right
export function f() {};

/ / right
function f() {}
export {f};
Copy the code

In addition, the interface output by the export statement has a dynamic binding relationship with its corresponding value, that is, the real-time value inside the module can be obtained through this interface.

export var foo = 'bar';
setTimeout(() = > foo = 'baz'.500);
Copy the code

The above code outputs a variable foo with a value of bar, which becomes baz after 500 milliseconds.

This is completely different from the CommonJS specification. The CommonJS Module outputs a cache of values with no dynamic updates, as described in the Module Loading Implementation section below.

Finally, the export command can appear anywhere in the module, as long as it is at the top of the module. If you are in block-level scope, an error is reported, as is the import command in the next section. This is because you can’t do static optimizations when you’re in a conditional block, which goes against the design of the ES6 module.

function foo() {
  export default 'bar' // SyntaxError
}
foo()
Copy the code

In the above code, the export statement is placed inside the function, resulting in an error.

import

After the module’s external interface is defined using the export command, other JS files can load the module using the import command.

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

function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}
Copy the code

The import command of the above code is used to load the profile.js file and enter variables from it. The import command accepts a pair of curly braces specifying the name of a variable to import from another module. The variable name inside the braces must be the same as the name of the external interface of the imported module (profile.js).

If you want to rename the input variable, the import command uses the as keyword to rename the input variable.

import { lastName as surname } from './profile.js';
Copy the code

The variables entered by the import command are read-only because it is by nature an input interface. That is, it is not allowed to rewrite interfaces in scripts that load modules.

import {a} from './xxx.js'

a = {}; // Syntax Error : 'a' is read-only;
Copy the code

In the above code, the script loads variable A and reassigns it to an error because A is a read-only interface. However, if A is an object, overwriting a’s properties is allowed.

import {a} from './xxx.js'

a.foo = 'hello'; // It is valid
Copy the code

In the code above, the property of A can be successfully overwritten, and other modules can also read the overwritten value. However, this method is very difficult to check, it is recommended that all input variables, as a completely read-only, do not easily change its properties.

The from after import specifies the location of the module file, which can be a relative or absolute path. If you don’t have a path but just a module name, you must have a configuration file that tells the JavaScript engine where the module is.

import { myMethod } from 'util';
Copy the code

In the above code, util is the module file name. Since there is no path, you must configure it to tell the engine how to fetch the module.

Note that the import command is promoted to the top of the module and executed first.

foo();

import { foo } from 'my_module';
Copy the code

The above code does not report an error because import is executed before foo is called. The essence of this behavior is that the import command is executed at compile time, before the code runs.

Overall loading of modules (*)

// main.js

import { area, circumference } from './circle';

console.log('Circle area:' + area(4));
console.log('Circumference:' + circumference(14));
Copy the code

The above is written to specify the methods to be loaded one by one. The overall loading is written as follows.

import * as circle from './circle';

console.log('Circle area:' + circle.area(4));
console.log('Circumference:' + circle.circumference(14));
Copy the code

export default

The export default command specifies the default output for the module. This is a unique value. A module can have only one default output. It is not recommended for development.

/ / the first group
export default function crc32() { / / output
  // ...
}

import crc32 from 'crc32'; / / input

/ / the second group
export function crc32() { / / output
  // ...
};

import {crc32} from 'crc32'; / / input
Copy the code

see

Ruan Yifeng – Introduction to ES6