>  기사  >  웹 프론트엔드  >  es6의 모듈성을 자세히 설명하는 기사

es6의 모듈성을 자세히 설명하는 기사

青灯夜游
青灯夜游앞으로
2022-11-02 20:17:171623검색

es6의 모듈성을 자세히 설명하는 기사

CommonJs에는 뛰어난 기능이 많이 있습니다. 아래에서 간단히 살펴보겠습니다. CommonJs 有很多优秀的特性,下面我们再简单的回顾一下:

  • 模块代码只在加载后运行;

  • 模块只能加载一次;

  • 模块可以请求加载其他模块;

  • 支持循环依赖;

  • 模块可以定义公共接口,其他模块可以基于这个公共接口观察和交互;

天下苦 CommonJs 久矣


Es Module 的独特之处在于,既可以通过浏览器原生加载,也可以与第三方加载器和构建工具一起加载。

支持 Es module 模块的浏览器可以从顶级模块加载整个依赖图,且是异步完成。浏览器会解析入口模块,确定依赖,并发送对依赖模块的请求。这些文件通过网络返回后,浏览器就会解析它们的依赖,,如果这些二级依赖还没有加载,则会发送更多请求。

这个异步递归加载过程会持续到整个应用程序的依赖图都解析完成。解析完成依赖图,引用程序就可以正式加载模块了。

Es Module 不仅借用了 CommonJsAMD 的很多优秀特性,还增加了一些新行为:

  • Es Module 默认在严格模式下执行;

  • Es Module 不共享全局命名空;

  • Es Module 顶级的 this 的值是 undefined(常规脚本是window);

  • 模块中的 var 声明不会添加到 window 对象;

  • Es Module 是异步加载和执行的;

export 和 import


  • 模块功能主要由两个命令构成: exportsimport

  • export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

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 };
  • 通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。
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 };

export 的错误使用

  • 导出语句必须在模块顶级,不能嵌套在某个块中:
if(true){
export {...};
}
  • export 必须提供对外的接口:
// 1只是一个值,不是一个接口
export 1

// moment只是一个值为1的变量
const moment = 1
export moment

// function和class的输出,也必须遵守这样的写法
function foo(x, y) {
    return x+y
}
export foo

import的基本使用

  • 使用 export 命令定义了模块的对外接口以后,其他js文件就可以通过 import 命令加载整个模块
import {foo,age,nickname} form '模块标识符'
  • 模块标识符可以是当前模块的相对路径,也可以是绝对路径,也可以是纯字符串,但不能是动态计算的结果,例如凭借的字符串。
  • import 命令后面接受一个花括弧,里面指定要从其他模块导入的变量名,而且变量名必须与被导入模块的对外接口的名称相同。
  • 对于导入的变量不能对其重新赋值,因为它是一个只读接口,如果是一个对象,可以对这个对象的属性重新赋值。导出的模块可以修改值,导入的变量也会跟着改变。

es6의 모듈성을 자세히 설명하는 기사

  • 从上图可以看得出来,对象的属性被重新赋值了,而变量的则报了 Assignment to constant variable 的类型错误。
  • 如果模块同时导出了命名导出和默认导出,则可以在 import 语句中同时取得它们。可以依次列出特定的标识符来取得,也可以使用 * 来取得:
// 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";

动态 import

  • 标准用法的 import 导入的模块是静态的,会使所有被导入的模块,在加载时就被编译(无法做到按需编译,降低首页加载速度)。有些场景中,你可能希望根据条件导入模块或者按需导入模块,这时你可以使用动态导入代替静态导入。
  • 关键字 import 可以像调用函数一样来动态的导入模块。以这种方式调用,将返回一个 promise
    • 모듈 코드는 Run에만 있습니다. 로드 후;

  • 모듈은 한 번만 로드할 수 있습니다.
  • 모듈은 다른 모듈을 로드하도록 요청할 수 있습니다.🎜
  • 🎜순환 종속성을 지원합니다.🎜🎜
  • 🎜모듈은 공개를 정의할 수 있습니다. 인터페이스를 사용하면 다른 모듈이 이 공개 인터페이스를 기반으로 관찰하고 상호 작용할 수 있습니다. 🎜🎜🎜

    🎜天下狠 CommonJs 久矣🎜


    🎜Es 모듈은 두 가지 모두에서 고유합니다. 브라우저나 타사 로더 및 빌드 도구를 통해 기본적으로 로드할 수 있습니다. 🎜🎜Es 모듈 모듈을 지원하는 브라우저는 최상위 모듈에서 전체 종속성 그래프를 비동기적으로 로드할 수 있습니다. 브라우저는 항목 모듈을 구문 분석하고, 종속성을 결정하고, 종속 모듈에 대한 요청을 보냅니다. 이러한 파일이 네트워크를 통해 반환된 후 브라우저는 해당 종속성을 해결하고 이러한 보조 종속성이 로드되지 않은 경우 추가 요청이 전송됩니다. 🎜🎜이 비동기 재귀 로딩 프로세스는 전체 애플리케이션의 종속성 그래프가 해결될 때까지 계속됩니다. 종속성 그래프가 구문 분석된 후 참조 프로그램은 공식적으로 모듈을 로드할 수 있습니다. 🎜🎜Es 모듈CommonJsAMD의 많은 뛰어난 기능을 빌릴 뿐만 아니라 몇 가지 새로운 동작도 추가합니다. 🎜
    • 🎜Es 모듈은 기본적으로 엄격 모드에서 실행됩니다. 🎜🎜
    • 🎜Es 모듈은 전역을 공유하지 않습니다. 🎜🎜
    • 🎜Es 모듈 최상위 this의 값은 정의되지 않았습니다(일반 스크립트는 window)입니다. code>); 🎜🎜
    • 🎜모듈의 var 선언은 window 객체에 추가되지 않습니다. 🎜🎜
    • 🎜Es 모듈 는 비동기적으로 로드되어 실행됩니다.🎜🎜🎜

      🎜export 및 import🎜


      • 🎜모듈 기능은 주로 두 가지 명령으로 구성됩니다: exports 및 <code>가져오기. 🎜🎜
      • export 명령은 모듈의 외부 인터페이스를 지정하는 데 사용되며, import 명령은 다른 모듈에서 제공하는 기능을 가져오는 데 사용됩니다. 🎜🎜🎜🎜🎜export의 기본 사용법🎜🎜🎜
        • 기본 내보내기 형식: 🎜🎜
          import("./foo.js").then((module) => {
            const { default: foo, bar, baz } = module;
            console.log(foo); // [Function: foo]
            console.log(bar); // 777
            console.log(baz); // moment
          });
          • 물론 다음 형식으로도 작성할 수도 있습니다. 🎜🎜
            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);
              객체와 함수를 외부로 내보내기 🎜🎜
              const p = new Promise((resolve, reject) => {
                resolve(777);
              });
              
              const result = await p;
              
              console.log(result); // 777正常输出
              • 보통 export로 출력되는 변수는 원래 이름이지만 를 키워드 이름을 바꿉니다. 🎜🎜<pre class="brush:js;toolbar:false;">// 错误 import { &amp;#39;b&amp;#39; + &amp;#39;ar&amp;#39; } from &amp;#39;./foo.js&amp;#39;; // 错误 let module = &amp;#39;./foo.js&amp;#39;; import { bar } from module; // 错误 if (x === 1) { import { bar } from &amp;#39;./foo.js&amp;#39;; } else { import { foo } from &amp;#39;./foo.js&amp;#39;; }</pre><ul><li>기본 내보내기, 모듈에는 하나의 기본 내보내기만 있을 수 있다는 점에 유의할 가치가 있습니다: 🎜🎜<pre class="brush:js;toolbar:false;">&lt;script type=&quot;module&quot; src=&quot;./main.mjs&quot;&gt;&lt;/script&gt; &lt;script type=&quot;module&quot;&gt;&lt;/script&gt;</pre>🎜🎜🎜export 잘못된 사용🎜🎜🎜<ul><li>내보내기 문은 다음 위치에 있어야 합니다. 모듈 최상위 수준, 블록에 중첩될 수 없음: 🎜🎜<pre class="brush:js;toolbar:false;"> &lt;script type=&quot;module&quot;&gt; console.log(&quot;模块情况下的&quot;); &lt;/script&gt; &lt;script src=&quot;./main.js&quot; type=&quot;module&quot; defer&gt;&lt;/script&gt; &lt;script&gt; console.log(&quot;正常 script标签&quot;); &lt;/script&gt;</pre><ul><li> <code>export 외부 인터페이스를 제공해야 함: 🎜🎜
                    <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>
                🎜🎜🎜import의 기본 사용🎜🎜🎜
                • export 명령을 사용하여 모듈의 외부 인터페이스를 정의한 후 다른 js 파일에서 import 명령을 통해 전체 모듈을 로드할 수 있습니다🎜🎜
                  // 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");
                  • 모듈 식별 문자는 현재 모듈에 대한 상대 경로, 절대 경로 또는 순수 문자열일 수 있지만 종속 문자열과 같은 동적 계산의 결과일 수는 없습니다. 🎜
                  • import 이 명령은 다른 모듈에서 가져올 변수 이름을 지정하는 중괄호를 허용하며, 변수 이름은 가져온 모듈의 외부 인터페이스 이름과 동일해야 합니다. . 🎜
                  • 가져온 변수는 읽기 전용 인터페이스이므로 재할당할 수 없습니다. 객체인 경우 해당 객체의 속성을 재할당할 수 있습니다. 내보낸 모듈은 값을 수정할 수 있으며 가져온 변수도 이에 따라 변경됩니다. 🎜🎜🎜es6의 모듈성을 자세히 설명하는 기사🎜
                    • 위 그림에서 볼 수 있듯이 객체의 속성이 재할당된 반면, 변수에서는 상수 변수에 할당'이라는 유형 오류가 보고되었습니다. >. 🎜
                    • 모듈이 명명된 내보내기와 기본 내보내기를 모두 내보내는 경우 import 문에서 동시에 가져올 수 있습니다. 이를 얻기 위해 특정 식별자를 나열하거나 *를 사용하여 얻을 수 있습니다. 🎜🎜rrreee🎜🎜🎜dynamic import🎜🎜🎜
                      • 의 표준 사용법 import 가져온 모듈은 정적이므로 가져온 모든 모듈이 로드될 때 컴파일됩니다(요청 시 컴파일을 수행할 수 없으므로 홈 페이지의 로드 속도가 느려집니다). 일부 시나리오에서는 조건이나 요청 시 모듈을 가져올 수 있습니다. 이 경우 정적 가져오기 대신 동적 가져오기를 사용할 수 있습니다. 🎜<li> <code>import 키워드는 함수를 호출하는 것처럼 모듈을 동적으로 가져올 수 있습니다. 이런 식으로 호출하면 promise가 반환됩니다. 🎜🎜rrreee🎜🎜🎜최상위 대기 사용🎜🎜🎜
                        • 在经典脚本中使用 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是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
                        // 错误
                        import { &#39;b&#39; + &#39;ar&#39; } from &#39;./foo.js&#39;;
                        
                        // 错误
                        let module = &#39;./foo.js&#39;;
                        import { bar } from module;
                        
                        // 错误
                        if (x === 1) {
                          import { bar } from &#39;./foo.js&#39;;
                        } else {
                          import { foo } from &#39;./foo.js&#39;;
                        }

                        在浏览器中使用 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>

                        es6의 모듈성을 자세히 설명하는 기사

                        • 在浏览器中,引入相同的 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>

                        es6의 모듈성을 자세히 설명하는 기사

                        模块的默认延迟


                        • 默认情况下,nomodule 脚本会阻塞 HTML 解析。你可以通过添加 defer 属性来解决此问题,该属性是等到 HTML 解析完成之后才执行。

                        es6의 모듈성을 자세히 설명하는 기사

                        • deferasync 是一个可选属性,他们只可以选择其中一个,在 nomodule 脚本下,defer 等到 HTML 解析完才会解析当前脚本,而 async 会和 HTML 并行解析,不会阻塞 HTML 的解析,模块脚本可以指定 async 属性,但对于 defer 无效,因为模块默认就是延迟的。
                        • 对于模块脚本,如果存在 async 属性,模块脚本及其所有依赖项将于解析并行获取,并且模块脚本将在它可用时进行立即执行。

                        Es Module 和 Commonjs 的区别


                        讨论 Es Module 模块之前,必须先了解 Es ModuleCommonjs 完全不同,它们有三个完全不同:

                        • 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

                        模块记录(Module Record) 封装了关于单个模块(当前模块)的导入和导出的结构信息。此信息用于链接连接模块集的导入和导出。一个模块记录包括四个字段,它们只在执行模块时使用。其中这四个字段分别是:

                        • Realm: 创建当前模块的作用域;

                        • Environment:模块的顶层绑定的环境记录,该字段在模块被链接时设置;

                        • 네임스페이스: 모듈 네임스페이스 개체는 모듈 내보내기 바인딩에 대한 런타임 속성 기반 액세스를 제공하는 모듈 네임스페이스 외부 개체입니다. 모듈 네임스페이스 객체에는 생성자가 없습니다.

                          Namespace:模块命名空间对象是模块命名空间外来对象,它提供对模块导出绑定的基于运行时属性的访问。模块命名空间对象没有构造函数;
                        • HostDefined:字段保留,以按 host environments 使用,需要将附加信息与模块关联。

                        Module Environment Record

                        • 模块环境记录是一种声明性环境记录,用于表示ECMAScript模块的外部作用域。除了普通的可变和不可变绑定之外,模块环境记录还提供了不可变的 import 绑定,这些绑定提供了对存在于另一个环境记录中的目标绑定的间接访问。

                        不可变绑定就是当前的模块引入其他的模块,引入的变量不能修改,这就是模块独特的不可变绑定。

                        Es Module 的解析流程


                        在开始之前,我们先大概了解一下整个流程大概是怎么样的,先有一个大概的了解:

                        • 阶段一:构建(Construction),根据地址查找 js 文件,通过网络下载,并且解析模块文件为 Module Record;

                        • 阶段二:实例化(Instantiation),对模块进行实例化,并且分配内存空间,解析模块的导入和导出语句,把模块指向对应的内存地址;

                        • 阶段三:运行(Evaluation),运行代码,计算值,并且将值填充到内存地址中;

                        Construction 构建阶段

                        • loader 负责对模块进行寻址及下载。首先我们修改一个入口文件,这在 HTML 中通常是一个 <script type="module"></script> 的标签来表示一个模块文件。

                        es6의 모듈성을 자세히 설명하는 기사

                        • 模块继续通过 import语句声明,在 import声明语句中有一个 模块声明标识符(ModuleSpecifier),这告诉 loader 怎么查找下一个模块的地址。

                        es6의 모듈성을 자세히 설명하는 기사

                        • 每一个模块标识号对应一个 模块记录(Module Record),而每一个 模块记录 包含了 JavaScript代码执行上下文ImportEntriesLocalExportEntriesIndirectExportEntriesStarExportEntries。其中 ImportEntries 值是一个 ImportEntry Records 类型,而 LocalExportEntriesIndirectExportEntriesStarExportEntries 是一个 ExportEntry Records 类型。

                        ImportEntry Records

                        • 一个 ImportEntry Records 包含三个字段 ModuleRequestImportNameLocalName;

                        ModuleRequest: 一个模块标识符(ModuleSpecifier);

                        ImportName: 由 ModuleRequest 模块标识符的模块导出所需绑定的名称。值 namespace-object 表示导入请求是针对目标模块的命名空间对象的;

                        LocalName: 用于从导入模块中从当前模块中访问导入值的变量;

                        • 详情可参考下图:es6의 모듈성을 자세히 설명하는 기사
                        • 下面这张表记录了使用 import 导入的 ImportEntry Records
                        HostDefined: 호스트 환경에서 사용하도록 예약된 필드입니다. 모듈과 연결하려면 추가 정보가 필요합니다. 모듈 환경 기록
                        불변 바인딩은 현재 모듈이 다른 모듈을 도입하고, 도입된 변수를 수정할 수 없다는 것을 의미합니다. 이는 모듈의 고유한 불변 바인딩입니다.

                        Es 모듈 구문 분석 프로세스


                        2단계: 인스턴스화(인스턴스화 code>) , 모듈을 인스턴스화하고, 메모리 공간을 할당하고, 모듈의 가져오기 및 내보내기 문을 구문 분석하고, 모듈을 해당 메모리 주소로 지정합니다.구성 구성 단계
                        es6의 모듈성을 자세히 설명하는 기사 p>
                          모듈은 import 문을 통해 계속 선언됩니다. import에 모듈 선언 식별자(ModuleSpecifier)가 있습니다. 이 문장은 loader에게 다음 모듈의 주소를 찾는 방법을 알려줍니다. es6의 모듈성을 자세히 설명하는 기사 p>
                          A ImportEntry Records에는 ModuleRequest, <code>ImportName, LocalName;ModuleRequest: 모듈 식별자(ModuleSpecifier);ImportName: ModuleRequest 모듈 식별자의 모듈 내보내기에 필요한 바인딩 이름입니다. namespace-object 값은 가져오기 요청이 대상 모듈의 네임스페이스 개체에 대한 것임을 나타냅니다.자세한 내용은 아래 사진을 참고해주세요:es6의 모듈성을 자세히 설명하는 기사다음 표에는 import를 사용하여 가져온 ImportEntry Records 필드의 인스턴스가 기록되어 있습니다. Import 문 양식)모듈 식별자(ModuleRequest)가져오기 이름(ImportName)로컬 이름(LocalName)
                          모듈 환경 기록은 선언적 환경 기록입니다. ECMAScript 모듈의 외부 범위를 나타내는 데 사용됩니다. 일반적인 변경 가능 및 불변 바인딩 외에도 모듈 환경 레코드는 다른 환경 레코드 간접 액세스에 존재하는 대상에 대한 바인딩을 제공하는 변경 불가능한 import 바인딩도 제공합니다.
                          시작하기 전에 전체 프로세스가 어떻게 생겼는지 대략적으로 살펴보겠습니다. 일반적인 이해:
                            1단계: 건설(건설), 주소에 따라 검색 js 파일을 인터넷을 통해 다운로드하고 모듈 파일을 모듈 레코드로 구문 분석합니다.
                            3단계: 실행(평가 code>), 코드를 실행하고, 값을 계산하고, 그 값을 메모리 주소에 채웁니다; loader는 모듈 주소 지정 및 다운로드를 담당합니다. 먼저 HTML에서 일반적으로 모듈 파일을 나타내는 <script type="module"></script> 태그인 항목 파일을 수정합니다.
                            각 모듈 식별 번호는 모듈 레코드에 해당하며 각 모듈 레코드에는 JavaScript 코드, 실행 컨텍스트가 포함되어 있습니다. , ImportEntries, LocalExportEntries, IndirectExportEntries, StarExportEntries. ImportEntries 값은 ImportEntry Records 유형이고 LocalExportEntries, IndirectExportEntries, StarExportEntries ExportEntry Records 유형입니다.

                            ImportEntry 레코드

                              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"🎜🎜"효과"🎜🎜🎜🎜

                                ExportEntry Records

                                • ExportEntry Records에는 ExportName, ModuleRequest, ImportName, ImportEntry Records
                              • 와 다른 code>LocalName
                  에는 추가 ExportName이 있습니다.

                ExportName: 내보낼 때 이 모듈이 바인딩하는 데 사용하는 이름입니다. ExportEntry Records 包含四个字段 ExportNameModuleRequestImportNameLocalName,和 ImportEntry Records不同的是多了一个 ExportName

            ExportName: 此模块用于导出时绑定的名称。

            • 下面这张表记录了使用 export 导出的 ExportEntry Records

              다음 표에는 내보내기를 사용하여 내보낸 ExportEntry 레코드 필드의 인스턴스가 기록되어 있습니다.

              로컬 이름

              "v""f"default"default""x""v"nullnullnullnull
              내보내기 선언 내보내기 이름 모듈 식별자 가져오기 이름
              export var v; "v" null null
              내보내기 기본 함수 f() {} "기본값" null null
              기본 기능 내보내기() {} "default" null null"
              기본값 42 내보내기 ; "default" null null"
              export {x}; "x" null null
              {v를 x로 내보내기 }; "x" null null
              "mod"에서 {x} 내보내기; "x" "mod" "x"
              "mod"에서 {v를 x로 내보내기; "x" "mod" "v"
              export * from "mod"; null "mod " all-but-default
              "mod"에서 ns로 * 내보내기; "ns "mod" all
              🎜🎜
            • 주제로 돌아가기

            • 현재 모듈 레코드를 구문 분석한 후에만 현재 모듈이 어떤 하위 모듈에 의존하는지 알 수 있으며 그런 다음 하위 해결이 필요합니다. 모듈, 하위 모듈을 얻은 다음 하위 모듈을 구문 분석하고 이 해결 프로세스를 계속해서 가져오고 분석합니다. 결과는 아래 그림과 같습니다. Module Record 之后,才能知道当前模块依赖的是那些子模块,然后你需要 resolve 子模块,获取子模块,再解析子模块,不断的循环这个流程 resolving -> fetching -> parsing,结果如下图所示:

            es6의 모듈성을 자세히 설명하는 기사

            • 这个过程也称为 静态分析,不会运行JavaScript代码,只会识别 exportimport 关键字,所以说不能在非全局作用域下使用 import,动态导入除外。
            • 如果多个文件同时依赖一个文件呢,这会不会引起死循环,答案是不会的。
            • loader 使用 Module Map 对全局的 MOdule Record 进行追踪、缓存这样就可以保证模块只被 fetch 一次,每个全局作用域中会有一个独立的 Module Map。

            MOdule Map 是由一个 URL 记录和一个字符串组成的key/value的映射对象。URL记录是获取模块的请求URL,字符串指示模块的类型(例如。“javascript”)。模块映射的值要么是模块脚本,null(用于表示失败的获取),要么是占位符值“fetching(获取中)”。

            es6의 모듈성을 자세히 설명하는 기사

            linking 链接阶段

            • 在所有 Module Record 被解析完后,接下来 JS 引擎需要把所有模块进行链接。JS 引擎以入口文件的 Module Record 作为起点,以深度优先的顺序去递归链接模块,为每个 Module Record 创建一个 Module Environment Record,用于管理 Module Record 中的变量。

            es6의 모듈성을 자세히 설명하는 기사

            • Module Environment Record 中有一个 Binding,这个是用来存放 Module Record 导出的变量,如上图所示,在该模块 main.js 处导出了一个 count 的变量,在 Module Environment Record 中的 Binding 就会有一个 count,在这个时候,就相当于 V8 的编译阶段,创建一个模块实例对象,添加相对应的属性和方法,此时值为 undefined 或者 null,为其分配内存空间。
            • 而在子模块 count.js 中使用了 import 关键字对 main.js 进行导入,而 count.jsimportmain.jsexport 的变量指向的内存位置是一致的,这样就把父子模块之间的关系链接起来了。如下图所示:

            es6의 모듈성을 자세히 설명하는 기사

            • 需要注意的是,我们称 export 导出的为父模块,import 引入的为子模块,父模块可以对变量进行修改,具有读写权限,而子模块只有读权限。

            Evaluation 求值阶段

            • 在模块彼此链接完之后,执行对应模块文件中顶层作用域的代码,确定链接阶段中定义变量的值,放入内存中。

            Es module 是如何解决循环引用的


            • Es Module 中有5种状态,分别为 unlinkedlinkinglinkedevaluatingevaluated,用循环模块记录(Cyclic Module Records)的 Status 字段来表示,正是通过这个字段来判断模块是否被执行过,每个模块只执行一次。这也是为什么会使用 Module Map 来进行全局缓存 Module Record 的原因了,如果一个模块的状态为 evaluated,那么下次执行则会自动跳过,从而包装一个模块只会执行一次。 Es Module 采用 深度优先

            🎜es6의 모듈성을 자세히 설명하는 기사🎜
              🎜이 프로세스 정적 분석이라고도 하며 JavaScript 코드를 실행하지 않으며 내보내기가져오기 키워드만 인식하므로 가져오기 코드는 동적 가져오기를 제외하고 비전역 범위에서 사용할 수 없습니다. 🎜🎜여러 파일이 동시에 하나의 파일에 의존하는 경우 무한 루프가 발생합니까? 대답은 '아니요'입니다. 🎜🎜<code>loader모듈 맵을 사용하여 전역 MOdule 레코드를 추적하고 캐시하여 모듈이 가져오기 >만 수행되도록 합니다. 일단 각 전역 범위에 독립적인 모듈 맵이 있게 됩니다. 🎜
            🎜MOdule Map은 URL 레코드와 문자열로 구성된 키/값 매핑 개체입니다. URL 레코드는 모듈을 가져오기 위한 요청 URL이며, 모듈 유형을 나타내는 문자열(예: "javascript")입니다. 모듈 맵의 값은 모듈 스크립트, null(가져오기 실패를 나타내는 데 사용됨) 또는 자리 표시자 값 "가져오기"입니다. 🎜
            🎜es6의 모듈성을 자세히 설명하는 기사🎜🎜링크 연결 단계🎜
              🎜모든 모듈 레코드가 구문 분석된 후, 그런 다음 JS 엔진은 모든 모듈을 연결해야 합니다. JS 엔진은 항목 파일의 모듈 레코드를 시작점으로 사용하여 깊이 우선 순서로 모듈을 재귀적으로 연결하고 각 모듈에 대한 <code>모듈 환경 레코드를 생성합니다. 레코드.code>, 모듈 레코드에서 변수를 관리하는 데 사용됩니다. 🎜
            🎜es6의 모듈성을 자세히 설명하는 기사🎜
              🎜모듈 환경 레코드에는 그림과 같이 모듈 레코드에서 내보낸 변수를 저장하는 데 사용되는 바인딩이 있습니다. 위의 경우 count 변수는 이 모듈의 main.js로 내보내지고 모듈 환경 레코드Binding count가 있을 것입니다. 이때는 V8의 컴파일 단계와 동일하며, 모듈 인스턴스 객체를 생성하고 해당 속성과 메서드를 추가합니다. 이번에는 정의되지 않음 또는 null이므로 메모리 공간을 할당하세요. 🎜🎜import 키워드는 하위 모듈 count.js에서 main.js를 가져오는 데 사용되며 count.js는 import 변수와 main.jsexport 변수가 가리키는 메모리 위치가 동일하므로 상위 및 하위 모듈 관계가 연결됩니다. 아래 그림과 같이: 🎜
            🎜es6의 모듈성을 자세히 설명하는 기사 🎜
              🎜 export가 내보내는 것을 상위 모듈이라고 부르고, import가 소개하는 것을 하위 모듈이라고 부릅니다. 상위 모듈은 읽기 및 쓰기 권한으로 변수를 수정할 수 있지만 하위 모듈은 읽기 권한만 갖습니다. 🎜
            🎜평가 평가 단계🎜
              🎜모듈이 서로 연결된 후 해당 모듈을 실행합니다. 모듈 파일 최상위 범위의 코드는 링크 단계에서 정의된 변수의 값을 결정하고 이를 메모리에 저장합니다. 🎜

            Es 모듈이 순환 참조를 해결하는 방법


              🎜🎜 Es 모듈 상태에는 5가지 유형이 있습니다. 각각 연결 해제, 연결, 연결, 평가평가를 사용하여 표현됩니다. 순환 모듈 레코드(Cyclic Module Records)의 상태 필드를 통해 각 모듈이 한 번만 실행되었는지 판단합니다. . 이것이 모듈 맵모듈 레코드를 전역적으로 캐시하는 데 사용되는 이유이기도 합니다. 모듈의 상태가 evaluated이면 다음 실행이 수행됩니다. 자동으로 건너뛰므로 모듈 패키징은 한 번만 실행됩니다. Es 모듈깊이 우선 방법을 사용하여 모듈 그래프를 탐색합니다. 각 모듈은 한 번만 실행되므로 무한 루프를 방지합니다. 🎜

          深度优先搜索算法(英语:Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。这个算法会尽可能深地搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

          es6의 모듈성을 자세히 설명하는 기사

          • 看下面的例子,所有的模块只会运行一次:
          // 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");
          • 通过 node 运行 main.js ,得出以下结果:

          es6의 모듈성을 자세히 설명하는 기사

          • 好了,这篇文章到这也就结束了。

          原文地址:https://juejin.cn/post/7166046272300777508

          【推荐学习:javascript高级教程

          위 내용은 es6의 모듈성을 자세히 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

          성명:
          이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제