# 一、模块加载机制

  • 编译时加载 es6-module
import { stat, exists, readFile } from 'fs';
//上面代码的实质是从fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。
  • 运行时加载 commonjs
// CommonJS模块
let { stat, exists, readFile } = require('fs');

// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
//上面代码的实质是整体加载fs模块(即加载fs的所有方法),生成一个对象(_fs),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。

# 二、export 命令

  • 常用语法
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
//等同于
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;

export {firstName, lastName, year};
  • 动态绑定
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
//上面代码输出变量foo,值为bar,500 毫秒之后变成baz。
//export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。

# 三、import 命令

  • 只读属性
//import命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import {a} from './xxx.js'
a = {};

a.foo = 'hello'; // 合法操作
//如果a是一个对象,改写a的属性是允许的。但是不建议
  • 静态执行
//由于import是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
// 报错
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';
}
  • 执行次数
//如果多次重复执行同一句import语句,那么只会执行一次,而不会执行多次。
import 'lodash';
import 'lodash';

import { foo } from 'my_module';
import { bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';

# 四、export default 命令

  • 使用,export default 只能使用一次
// export-default.js
export default function () {
  console.log('foo');
}

import customName from './export-default';
customName(); // 'foo'
//import命令可以为该匿名函数指定任意名字,需要注意的是,这时import命令后面,不使用大括号。
  • 默认输出与正常输出对比
// 第一组
export default function crc32() { // 输出
  // ...
}
import crc32 from 'crc32'; // 输入

// 第二组
export function crc32() { // 输出
  // ...
};
import {crc32} from 'crc32'; // 输入
  • 变量
// 正确
export var a = 1;

// 正确
var a = 1;
export default a;
//export default a的含义是将变量a的值赋给变量default

// 错误
export default var a = 1;
//正是因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。

// 正确
export default 42;
// 报错
export 42;
//上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定外对接口为default

import _, { each, each as forEach } from 'lodash';
//如果想在一条import语句中,同时输入默认方法和其他接口,可以写成下面这样。

# 五、复合写法

export { foo, bar } from 'my_module';

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

# 六、动态加载

const path = './' + fileName;
const myModual = require(path);
//因为require是运行时加载模块,import命令无法取代require的动态加载功能。

# 七、script 的 defer 与 async 的区别

defer 与 async 的区别是:defer 要等到整个页面在内存中正常渲染结束(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async 一旦下载完,渲染引擎就会中断渲染,执行这个脚本以后,再继续渲染。一句话,defer 是“渲染完再执行”,async 是“下载完就执行”。另外,如果有多个 defer 脚本,会按照它们在页面出现的顺序加载,而多个 async 脚本是不能保证加载顺序的。

# 八、ES6 模块与 CommonJS 模块的差异

  • CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用
  • CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。