ES6 Module module

1. Introduction to Module

1. History

Historically, JavaScript has not had a module system, and it is impossible to split a large program into small files that depend on each other, and then assemble them in simple ways.

Before ES6, the community developed some module loading schemes, the most important ones are CommonJS, CMD (seaJS) and AMD (RequireJS). ES6 implements module functions at the level of language standards and becomes a common module solution for browsers and servers.

Extension: node.js follows the CommonJS specification

2. Overview

ES6 introduces modularity, and its design idea is to determine the dependencies of the modules and the input and output variables at compile time.

The modularity of ES6 is divided into two modules: export @ and import.

3. Features

ES6 modules automatically enable strict mode, regardless of whether you add use strict; to the module header or not.

Various types of variables can be imported and exported in the module, such as functions, objects, strings, numbers, Boolean values, classes, etc.

Each module has its own context, and the variables declared in each module are local variables and will not pollute the global scope.

Each module is loaded only once (singleton). If you load the same file in the same directory, it will be directly read from the memory.

Two, Module usage

1.export command

A module is an independent file. All variables inside the file cannot be obtained from outside. If you want the external to be able to read a variable inside the module, you must use the export keyword to export the variable.

Two syntaxes of export:

//语法1
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;

//语法2(推荐)
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };

as keyword:
Usually, the variable exported by export is the original name, but you can use the as keyword to rename

function v1() { ... }
function v2() { ... }

export {
  v1 as streamV1,
  v2 as streamV2,
  v2 as streamLatestVersion
};

Note for export use: The
export 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;

//正确写法一
export var m = 1;
//正确写法二
var m = 1;
export {m};
//正确写法三
var n = 1;
export {n as m};

Dynamic value:
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);
上面代码输出变量foo,值为bar,500 毫秒之后变成baz。

2.import command

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

import { firstName, lastName, year } from './profile.js';
function setName(element) {
  element.textContent = firstName + ' ' + lastName;
}

The variable name in 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, use the as keyword in the import command to rename the input variable.

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

The import command has a boosting effect. It will be boosted to the head of the entire module and executed first.

foo();
import { foo } from 'my_module';
//上面的代码不会报错,因为import的执行早于foo的调用

3. Overall module loading

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.

// 分开加载
import { area, circumference } from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));

//整体加载
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));

4.export default command

  • In a file or module, there can be multiple export and import, and only one export default.
  • The default in export default is the corresponding export interface variable.
  • Export through export, add {} when importing, and export default is not required.
  • Export default The members exposed to the outside can be received with any variable.
//导出
function add(x, y) {
  return x * y;
}
export {add as default};
// 等同于
// export default add;

//导入
import { default as foo } from 'modules';
// 等同于
// import foo from 'modules';

5. Compound writing of export and import

If in a module, the same module is imported first and then exported, the import statement can be written together with the export statement.

export { foo, bar } from 'my_module';

// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };

It should be noted that after writing in one line, foo and bar are not actually imported into the current module, but it is equivalent to forwarding these two interfaces to the outside, causing the current module to not directly use foo and bar.

6. Cross-module constants

If you want to use a lot of constants, you can create a special constants directory, write various constants in different files, and save them in this 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, merge the constants output by these files into index.js.

// constants/index.js
export {db} from './db';
export {users} from './users';

When using it, just load index.js directly.

// script.js
import {db, users} from './constants/index';

7.import()

The ES2020 proposal introduces the import() function to support dynamic loading of modules.

if (condition) {
  import('moduleA').then(...);
} else {
  import('moduleB').then(...);
}

Three, Module loading implementation

1. Browser loading

(1). Traditional loading

In HTML pages, the browser passes

<!-- 页面内嵌的脚本 -->
<script type="application/javascript">
  // module code
</script>

<!-- 外部脚本 -->
<script type="application/javascript" src="path/to/myModule.js">
</script>

By default, the browser loads JavaScript scripts synchronously, that is, when the rendering engine encounters

<script src="path/to/myModule.js" defer></script>
<script src="path/to/myModule.js" async></script>

Defer means "execute after rendering", async means "execute after downloading".

(2).Module loading

The browser loads ES6 modules and also uses

<script type="module" src="./foo.js"></script>
<!-- 等同于 -->
<script type="module" src="./foo.js" defer></script>

2. The difference between ES6 module and CommonJS module

There are three major differences between ES6 modules and CommonJS modules:

  • The output of the CommonJS module is a copy of the value, and the output of the ES6 module is the reference of the value.
  • The CommonJS module is loaded at runtime, and the ES6 module is the output interface at compile time.
  • The require() of the CommonJS module is to load the module synchronously, and the import command of the ES6 module is to be loaded asynchronously, and there is an independent module dependency analysis stage.

The first difference is explained as follows: The
output of the CommonJS module is a copy of the value

// lib.js
var counter = 3;
function incCounter() {
  counter++;
}
module.exports = {
  counter: counter,
  incCounter: incCounter,
};

// main.js
var mod = require('./lib');
console.log(mod.counter);  // 3
mod.incCounter();
console.log(mod.counter); // 3

When the JS engine of the ES6 module performs static analysis of the script, it will generate a read-only reference when it encounters the module loading command import. If the original value changes, the value loaded by import will also change.

// lib.js
export let counter = 3;
export function incCounter() {
  counter++;
}

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

Four, Node.js module loading method

1 Overview

JavaScript now has two modules. One is ES6 module, referred to as ESM; the other is CommonJS module, referred to as CJS.

The CommonJS module is dedicated to Node.js and is not compatible with ES6 modules. Starting with Node.js v13.2, Node.js has enabled ES6 module support by default.

Node.js requires ES6 modules to use .mjs suffix file names. If you do not want to change the suffix name to .mjs, you can specify the type field as module in the package.json file of the project.

{
   "type": "module"
}

.mjs files are always loaded as ES6 modules, .cjs files are always loaded as CommonJS modules, and the loading of .js files depends on the setting of the type field in package.json

Note, try not to mix ES6 modules and CommonJS modules.

2. The main field of package.json

The package.json file has two fields to specify the entry file of the module: main and exports. For simpler modules, you can use only the main field to specify the entry file loaded by the module.

// ./node_modules/es-module-package/package.json
{
  "type": "module",
  "main": "./src/index.js"
}

3. The exports field of package.json

The exports field has higher priority than the main field. It has multiple uses.

(1). Subdirectory alias

The exports field of the package.json file can specify aliases for scripts or subdirectories.
Specify script alias

/ ./node_modules/es-module-package/package.json
{
  "exports": {
    "./submodule": "./src/submodule.js"
  }
}

import submodule from 'es-module-package/submodule';
// 加载 ./node_modules/es-module-package/src/submodule.js

Specify subdirectory alias

// ./node_modules/es-module-package/package.json
{
  "exports": {
    "./features/": "./src/features/"
  }
}

import feature from 'es-module-package/features/x.js';
// 加载 ./node_modules/es-module-package/src/features/x.js

Extended-strict mode

Strict mode has the following limitations:

  • Variables must be declared before use
  • Function parameters cannot have attributes with the same name, otherwise an error will be reported
  • Cannot use with statement
  • Cannot assign value to read-only attribute, otherwise an error will be reported
  • The prefix 0 cannot be used to represent an octal number, otherwise an error will be reported
  • Undeletable attributes cannot be deleted, otherwise an error will be reported
  • The variable delete prop cannot be deleted, an error will be reported, only the attribute delete global[prop] can be deleted
  • eval does not introduce variables in its outer scope
  • eval and arguments cannot be reassigned
  • arguments will not automatically reflect changes in function parameters
  • Cannot use arguments.callee
  • Cannot use arguments.caller
  • Prevent this from pointing to the global object
  • Cannot use fn.caller and fn.arguments to get the stack of function calls
  • Added reserved words (such as protected, static and interface)

Guess you like

Origin blog.csdn.net/qq_41466440/article/details/111245239