Module 的语法
Module 的语法
  概述
    历史上，JavaScript 一直没有模块（module）体系，无法将一个大程序拆分成互相依赖的小文件，再用简单的方法拼装起来
    在 ES6 之前，社区制定了一些模块加载方案，最主要的有 CommonJS 和 AMD 两种。前者用于服务器，后者用于浏览器
    ES6 在语言标准的层面上，实现了模块功能，而且实现得相当简单，完全可以取代 CommonJS 和 AMD 规范，成为浏览器和服务器通用的模块解决方案
    ES6 模块的设计思想是尽量的静态化，使得编译时就能确定模块的依赖关系，以及输入和输出的变量
    通过export命令显式指定输出的代码，再通过import命令输入
      // ES6模块
import { stat, exists, readFile } from 'fs';
    ES6 模块有以下好处
      静态加载，使得静态分析成为可能
      不再需要UMD模块格式了，将来服务器和浏览器都会支持 ES6 模块格式。目前，通过各种工具库，其实已经做到了这一点。
      将来浏览器的新 API 就能用模块格式提供，不再必须做成全局变量或者navigator对象的属性。
      不再需要对象作为命名空间（比如Math对象），未来这些功能可以通过模块提供。
  严格模式
    ES6 的模块自动采用严格模式，不管你有没有在模块头部加上"use strict"
    严格模式主要有以下限制
      变量必须声明后再使用
      函数的参数不能有同名属性，否则报错
      不能使用with语句
      不能对只读属性赋值，否则报错
      不能使用前缀 0 表示八进制数，否则报错
      不能删除不可删除的属性，否则报错
      不能删除变量delete prop，会报错，只能删除属性delete global[prop]
      eval不会在它的外层作用域引入变量
      eval和arguments不能被重新赋值
      arguments不会自动反映函数参数的变化
      不能使用arguments.callee
      不能使用arguments.caller
      禁止this指向全局对象
      不能使用fn.caller和fn.arguments获取函数调用的堆栈
      增加了保留字（比如protected、static和interface）
  export 命令
    export命令用于规定模块的对外接口
      // profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
      // profile.js
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
      export function multiply(x, y) {
return x * y;
};
    通常情况下，export输出的变量就是本来的名字，但是可以使用as关键字重命名
    需要特别注意的是，export命令规定的是对外的接口，必须与模块内部的变量建立一一对应关系
    function和class的输出，也必须遵守这样的写法
      // 报错
function f() {}
export f;
// 正确
export function f() {};
// 正确
function f() {}
export {f};
    export语句输出的接口，与其对应的值是动态绑定关系，即通过该接口，可以取到模块内部实时的值
      export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
      代码输出变量foo，值为bar，500 毫秒之后变成baz
    export命令可以出现在模块的任何位置，只要处于模块顶层就可以。如果处于块级作用域内，就会报错
  import 命令
    import命令用于输入其他模块提供的功能
    如果想为输入的变量重新取一个名字，import命令要使用as关键字，将输入的变量重命名
    import命令输入的变量都是只读的，因为它的本质是输入接口。也就是说，不允许在加载模块的脚本里面，改写接口
    但是，如果a是一个对象，改写a的属性是允许的
    import后面的from指定模块文件的位置，可以是相对路径，也可以是绝对路径。如果不带有路径，只是一个模块名，那么必须有配置文件，告诉 JavaScript 引擎该模块的位置。
      import { myMethod } from 'util';
      代码中，util是模块文件名，由于不带有路径，必须通过配置，告诉引擎怎么取到这个模块
    import命令具有提升效果，会提升到整个模块的头部，首先执行
    提升这种行为的本质是，import命令是编译阶段执行的，在代码运行之前
    由于import是静态执行，所以不能使用表达式和变量，这些只有在运行时才能得到结果的语法结构。
    import语句会执行所加载的模块
      import 'lodash'
    如果多次重复执行同一句import语句，那么只会执行一次，而不会执行多次
    目前阶段，通过 Babel 转码，CommonJS 模块的require命令和 ES6 模块的import命令，可以写在同一个模块里面，但是最好不要这样做。因为import在静态解析阶段执行，所以它是一个模块之中最早执行的
  模块的整体加载
    import * as circle from './circle';
console.log('圆面积：' + circle.area(4));
console.log('圆周长：' + circle.circumference(14));
  export default 命令
    export default命令，为模块指定默认输出
    其他模块加载该模块时，import命令可以为该匿名函数指定任意名字
    一个模块只能有一个默认输出，因此export default命令只能使用一次。所以，import命令后面才不用加大括号，因为只可能唯一对应export default命令
    本质上，export default就是输出一个叫做default的变量或方法，然后系统允许你为它取任意名字。
    正是因为export default命令其实只是输出一个叫做default的变量，所以它后面不能跟变量声明语句
    因为export default命令的本质是将后面的值，赋给default变量，所以可以直接将一个值写在export default之后
  export 与 import 的复合写法
    如果在一个模块之中，先输入后输出同一个模块，import语句可以与export语句写在一起。
    export { foo, bar } from 'my_module';
// 可以简单理解为
import { foo, bar } from 'my_module';
export { foo, bar };
    默认接口的写法如下
      export { default } from 'foo';
    具名接口改为默认接口的写法如下
    默认接口也可以改名为具名接口
      export { default as es6 } from './someModule'
    ES2020 之前，有一种import语句，没有对应的复合写法
      import * as someIdentifier from "someModule"
    ES2020补上了这个写法
      export * as ns from "mod";
  模块的继承
    模块之间也可以继承
    假设有一个circleplus模块，继承了circle模块
    也可以将circle的属性或方法，改名后再输出
      // circleplus.js
export { area as circleArea } from 'circle';
    加载上面模块的写法如下
      // main.js
import * as math from 'circleplus';
import exp from 'circleplus';
console.log(exp(math.e));
      代码中的import exp表示，将circleplus模块的默认方法加载为exp方法
  跨模块常量
    即一个值要被多个模块共享
  import()适用场合
    （1）按需加载
    （2）条件加载
    （3）动态的模块路径
  // 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
  // export-default.js
export default function () {
console.log('foo');
}
  // import-default.js
import customName from './export-default';
customName(); // 'foo
  // 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
  // 正确
export default 42;
// 报错
export 42;
  export { es6 as default } from './someModule';
// 等同于
import { es6 } from './someModule';
export default es6;
  // circleplus.js
export * from 'circle';
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
  // main.js
import { firstName, lastName, year } from './profile.js';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
  import { lastName as surname } from './profile.js';
  import {a} from './xxx.js'
a.foo = 'hello'; // 合法操作
  import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;