Module 的加载实现
Module 的加载实现
  浏览器加载
    <script type="module" src="./foo.js"></script>
    浏览器对于带有type="module"的<script>，都是异步加载，不会造成堵塞浏览器
    等同于打开了<script>标签的defer属性
    对于外部的模块脚本，有几点需要注意
      代码是在模块作用域之中运行，而不是在全局作用域运行。模块内部的顶层变量，外部不可见。
      模块脚本自动采用严格模式，不管有没有声明use strict。
      模块之中，可以使用import命令加载其他模块（.js后缀不可省略，需要提供绝对 URL 或相对 URL），也可以使用export命令输出对外接口。
      模块之中，顶层的this关键字返回undefined，而不是指向window。也就是说，在模块顶层使用this关键字，是无意义的。
      同一个模块如果加载多次，将只执行一次。
  ES6 模块与 CommonJS 模块的差异
    CommonJS 模块输出的是一个值的拷贝，ES6 模块输出的是值的引用。
    CommonJS 模块是运行时加载，ES6 模块是编译时输出接口。
    CommonJS 模块的require()是同步加载模块，ES6 模块的import命令是异步加载，有一个独立的模块依赖的解析阶段
  Node.js 的模块加载方法
    概述
      avaScript 现在有两种模块。一种是 ES6 模块，简称 ESM；另一种是 CommonJS 模块，简称 CJS
      CommonJS 模块是 Node.js 专用的，与 ES6 模块不兼容。语法上面，两者最明显的差异是，CommonJS 模块使用require()和module.exports，ES6 模块使用import和export。
      从 Node.js v13.2 版本开始，Node.js 已经默认打开了 ES6 模块支持
      Node.js 要求 ES6 模块采用.mjs后缀文件名。也就是说，只要脚本文件里面使用import或者export命令，那么就必须采用.mjs后缀名。Node.js 遇到.mjs文件，就认为它是 ES6 模块，默认启用严格模式，不必在每个模块文件顶部指定"use strict"
      如果不希望将后缀名改成.mjs，可以在项目的package.json文件中，指定type字段为module。
      如果这时还要使用 CommonJS 模块，那么需要将 CommonJS 脚本的后缀名都改成.cjs。如果没有type字段，或者type字段为commonjs，则.js脚本会被解释成 CommonJS 模块。
      总结为一句话：.mjs文件总是以 ES6 模块加载，.cjs文件总是以 CommonJS 模块加载，.js文件的加载取决于package.json里面type字段的设置
    package.json 的 main 字段
    package.json 的 exports 字段
    CommonJS 模块加载 ES6 模块
    ES6 模块加载 CommonJS 模块
    同时支持两种格式的模块
    Node.js 的内置模块
    加载路径
    内部变量
  循环加载
    指的是，a脚本的执行依赖b脚本，而b脚本的执行又依赖a脚本
    通常，“循环加载”表示存在强耦合，如果处理不好，还可能导致递归加载，使得程序无法执行，因此应该避免出现。
    对于 JavaScript 语言来说，目前最常见的两种模块格式 CommonJS 和 ES6，处理“循环加载”的方法是不一样的，返回的结果也不一样
    CommonJS 模块的循环加载
    ES6 模块的循环加载