CommonJs
has many excellent features. Let’s briefly review them below:
The module code is only loaded when After running;
The module can only be loaded once;
The module can request to load other modules;
Support circular dependencies;
Modules can define public interfaces, and other modules can observe and interact based on this public interface;
Es Module
is that it can be loaded natively through the browser or with third-party loaders and build tools load.
Browsers that support Es module
modules can load the entire dependency graph from the top-level module, and it is done asynchronously. The browser will parse the entry module, determine the dependencies, and send a request for the dependent module. After these files are returned over the network, the browser will resolve their dependencies, and if these secondary dependencies have not been loaded, more requests will be sent.
This asynchronous recursive loading process will continue until the entire application's dependency graph has been resolved. After the dependency graph is parsed, the reference program can officially load the module.
Es Module
not only borrows many excellent features of CommonJs
and AMD
, but also adds some new behaviors:
Es Module
Execute in strict mode by default;
##Es Module Does not share global namespace;
Es Module The value of the top-level
this is
undefined (the regular script is
window);
var declarations in modules are not added to the
window object;
Es Module is loaded and executed asynchronously;
and import
. The
import
command is used to import the functions provided by other modules.
Basic use of export
Basic form of export:export const nickname = "moment"; export const address = "广州"; export const age = 18;
const nickname = "moment"; const address = "广州"; const age = 18; export { nickname, address, age };
export function foo(x, y) { return x + y; } export const obj = { nickname: "moment", address: "广州", age: 18, }; // 也可以写成这样的方式 function foo(x, y) { return x + y; } const obj = { nickname: "moment", address: "广州", age: 18, }; export { foo, obj };
as
keyword.
const address = "广州"; const age = 18; export { nickname as name, address as where, age as old };
export default "foo"; export default { name: 'moment' } export default function foo(x,y) { return x+y } export { bar, foo as default };
Incorrect use of export
The export statement must be at the top level of the module and cannot be nested in a block:if(true){ export {...}; }
// 1只是一个值,不是一个接口 export 1 // moment只是一个值为1的变量 const moment = 1 export moment // function和class的输出,也必须遵守这样的写法 function foo(x, y) { return x+y } export foo
Basic usage of import
Usageimport
command.
import {foo,age,nickname} form '模块标识符'
The imported variable cannot be reassigned because it is a read-only interface. If it is an object, the properties of the object can be reassigned. Exported modules can modify values, and imported variables will also change accordingly.
If the module exports both named exports and default exports, they can be obtained at the same time in the *
to obtain them:
// foo.js export default function foo(x, y) { return x + y; } export const bar = 777; export const baz = "moment"; // main.js import { default as foo, bar, baz } from "./foo.js"; import foo, { bar, baz } from "./foo.js"; import foo, * as FOO from "./foo.js";
dynamic import
Standard usage
Keywords promise
will be returned.
import("./foo.js").then((module) => { const { default: foo, bar, baz } = module; console.log(foo); // [Function: foo] console.log(bar); // 777 console.log(baz); // moment });
Use top-level await import 的错误使用 在浏览器上,你可以通过将 默认情况下, 讨论 第二个差异是因为 在学习工作原理之前,我们不妨来认识一下相关的概念。 Module Record 模块记录( Module Environment Record Immutable binding means that the current module introduces other modules, and the introduced variables cannot be modified. This is the unique immutable binding of the module. Before we start, let’s first have a general idea of what the entire process is like. Understanding: Phase 1: Construction ( Phase 2: Instantiation ( Phase 3: Run ( Construction construction phase ModuleRequest: a module identifier ( ImportName: by LocalName: variable used to access the imported value from the current module from the imported module; ExportName: The name this module uses to bind when exporting. The following table records examples of Back to topic Only after parsing the current MOdule Map is a key/value mapping object composed of a URL record and a string. The URL record is the request URL to get the module, a string indicating the type of module (e.g. "javascript"). The value of the module map is either the module script, null (used to indicate a failed fetch), or the placeholder value "fetching". linking linking phase Evaluation evaluation phase The states are 好了,这篇文章到这也就结束了。 原文地址:https://juejin.cn/post/7166046272300777508 【推荐学习:javascript高级教程】 The above is the detailed content of An article explaining modularity in es6 in detail. For more information, please follow other related articles on the PHP Chinese website!await
必须在带有 async
的异步函数中使用,否则会报错:const p = new Promise((resolve, reject) => {
resolve(111);
});
// SyntaxError: await is only valid in async functions and the top level bodies of modules
const result = await p;
console.log(result);
Top-level await
:const p = new Promise((resolve, reject) => {
resolve(777);
});
const result = await p;
console.log(result); // 777正常输出
import
是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。// 错误
import { 'b' + 'ar' } from './foo.js';
// 错误
let module = './foo.js';
import { bar } from module;
// 错误
if (x === 1) {
import { bar } from './foo.js';
} else {
import { foo } from './foo.js';
}
在浏览器中使用 Es Module
type
属性设置为 module
用来告知浏览器将 script
标签视为模块。<script type="module" src="./main.mjs"></script>
<script type="module"></script>
defer
的方式延迟你的 nomodule
脚本: <script type="module">
console.log("模块情况下的");
</script>
<script src="./main.js" type="module" defer></script>
<script>
console.log("正常 script标签");
</script>
nomodule
脚本会被执行多次,而模块只会被执行一次: <script src="./foo.js"></script>
<script src="./foo.js"></script>
<script type="module" src="./main.js"></script>
<script type="module" src="./main.js"></script>
<script type="module" src="./main.js"></script>
模块的默认延迟
nomodule
脚本会阻塞 HTML
解析。你可以通过添加 defer
属性来解决此问题,该属性是等到 HTML
解析完成之后才执行。defer
和 async
是一个可选属性,他们只可以选择其中一个,在 nomodule
脚本下,defer
等到 HTML
解析完才会解析当前脚本,而 async
会和 HTML
并行解析,不会阻塞 HTML
的解析,模块脚本可以指定 async
属性,但对于 defer
无效,因为模块默认就是延迟的。async
属性,模块脚本及其所有依赖项将于解析并行获取,并且模块脚本将在它可用时进行立即执行。Es Module 和 Commonjs 的区别
Es Module
模块之前,必须先了解 Es Module
与 Commonjs
完全不同,它们有三个完全不同:CommonJS
模块输出的是一个值的拷贝,Es Module
输出的是值的引用;CommonJS
模块是运行时加载,Es Module
是编译时输出接口。CommonJS
模块的 require()
是同步加载模块,ES6 模块的import
命令是异步加载,有一个独立的模块依赖的解析阶段。CommonJS
加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。而 Es Module
不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。Commonjs
输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。具体可以看上一篇写的文章。Es Module
的运行机制与 CommonJS
不一样。JS引擎
对脚本静态分析的时候,遇到模块加载命令import
,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,import
就是一个连接管道,原始值变了,import
加载的值也会跟着变。因此,Es Module
是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。Es Module 工作原理的相关概念
Module Record
) 封装了关于单个模块(当前模块)的导入和导出的结构信息。此信息用于链接连接模块集的导入和导出。一个模块记录包括四个字段,它们只在执行模块时使用。其中这四个字段分别是:Realm
: 创建当前模块的作用域;Environment
:模块的顶层绑定的环境记录,该字段在模块被链接时设置;Namespace
: A module namespace object is a module namespace foreign object that provides runtime property-based access to module export bindings. The module namespace object has no constructor; HostDefined
: Field reserved for use by host environments
, additional information is required to be associated with the module . import
bindings that provide binding to a target that exists in another environment record indirect access. Es Module parsing process
Construction
), find the js
file according to the address, download it through the network, and parse it The module file is Module Record
;Instantiation
), instantiate the module and allocate memory space. Parse the import and export statements of the module and point the module to the corresponding memory address; Evaluation
), run the code, calculate the value, and The value is filled into the memory address;loader
Responsible for addressing and downloading modules. First we modify an entry file, which in HTML
is usually a <script type="module"></script>
tag to represent a module file. import
statement. There is a module declaration identifier in the import
declaration statement. Character (ModuleSpecifier
), which tells loader
how to find the address of the next module. module record (Module Record)
, and each module record
Contains JavaScript code
, execution context
, ImportEntries
, LocalExportEntries
, IndirectExportEntries
, StarExportEntries
. The ImportEntries
value is a ImportEntry Records
type, and LocalExportEntries
, IndirectExportEntries
, StarExportEntries
is a ExportEntry Records
type. ImportEntry Records
ImportEntry Records
contains three fields ModuleRequest
, ImportName
, LocalName
;ModuleSpecifier
); ModuleRequest
The name of the required binding for the module export of the module identifier. The value namespace-object
indicates that the import request is for the namespace object of the target module;ImportEntry Records
fields imported using import
:Import Statement (Import Statement Form) Module Identifier (ModuleRequest) Import Name (ImportName) LocalName(LocalName) ##import React from "react"; "react" "default " "React" import * as Moment from "react"; "react" namespace-obj "Moment" import {useEffect} from "react"; "react" "useEffect" "useEffect"import {useEffect as effect } from "react"; "react" "useEffect" "effect"ExportEntry Records
ExportEntry Records
contains four fields ExportName
, ModuleRequest
,ImportName
, LocalName
, and ImportEntry Records
are different in that there is an additional ExportName
. ExportEntry Records
fields exported using export
:Export declaration Export name Module identifier Import name Local name export var v; "v" null null "v" export default function f() {} "default" null null "f" export default function () {} "default" null null "default" export default 42; "default" null null "default" export {x}; "x" null null "x" export {v as x}; "x" null null "v" export {x} from "mod"; "x" "mod" "x" null export {v as x} from "mod"; "x" "mod" "v" null export * from "mod"; null "mod" all-but-default null ##export * as ns from "mod"; " ns "mod" all null Module Record
can we know which submodules the current module depends on , then you need to resolve
the submodule, obtain the submodule, then parse the submodule, and continuously cycle this process resolving -> fetching -> parsing. The result is as shown in the figure below: static analysis
. It will not run JavaScript code and will only identify export
and import
keyword, so import
cannot be used in non-global scope, except for dynamic import. loader
Use Module Map
to track and cache the global MOdule Record
to ensure that the module is only fetch
Once, there will be an independent Module Map in each global scope. Module Record
After being parsed, the JS engine needs to link all modules. The JS engine takes the Module Record
of the entry file as the starting point, recursively links the modules in depth-first order, and creates a Module Environment Record
for each Module Record
. Used to manage variables in Module Record
.
##It should be noted that we call has a
Binding, which is used to store
Module The variables exported by Record, as shown in the figure above, export a variable of
count at the module
main.js, in
Module Environment Record The
Binding will have a
count. At this time, it is equivalent to the compilation phase of
V8, creating a module instance object, adding the corresponding attributes and Method, the value at this time is
undefined or
null, allocate memory space for it.
keyword is used in the submodule
count.js to import
main.js, and
count. The import
of js and the
export variable of
main.js point to the same memory location, thus linking the relationship between the parent and child modules. Woke up. As shown in the figure below:
export
import
The submodule is introduced. The parent module can modify variables and has read and write permissions, while the submodule only has read permissions. How Es module solves circular references
There are 5 in
unlinked
, linking
, linked
, evaluating
and evaluated
respectively, recorded using the loop module ( Cyclic Module Records
) is represented by the Status
field. It is through this field that it is judged whether the module has been executed. Each module is only executed once. This is also why Module Map
is used for global caching Module Record
. If the status of a module is evaluated
, then the next execution will automatically Skip, thus wrapping a module will only be executed once. Es Module
uses the depth first
method to traverse the module graph. Each module is only executed once, which avoids an infinite loop. </li></ul><blockquote><p>深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。</p></blockquote><p><img src="https://img.php.cn/upload/article/000/000/024/c1be30abed784f42b5efc75e5b4256e1-11.png" alt="An article explaining modularity in es6 in detail" loading="lazy"/></p><ul><li>看下面的例子,所有的模块只会运行一次:</li></ul><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">// main.js
import { bar } from "./bar.js";
export const main = "main";
console.log("main");
// foo.js
import { main } from "./main.js";
export const foo = "foo";
console.log("foo");
// bar.js
import { foo } from "./foo.js";
export const bar = "bar";
console.log("bar");</pre><div class="contentsignin">Copy after login</div></div><ul><li>通过 <code>node
运行 main.js
,得出以下结果: