Module语法
CommonJS模块
js
let { readfile } = require('fs')
# 等同于
let _fs = require('fs')
let readfile = _fs.readfile //这种加载称为"运行时加载"
ES6模块
js
import { stat, exists, readFile } from 'fs';
这种加载称为"编译时加载"或者静态加载
静态加载带来的各种好处
- 效率要比 CommonJS 模块的加载方式高。
- 能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
- 不再需要
UMD
模块格式了,将来服务器和浏览器都会支持 ES6 模块格式。 - 将来浏览器的新 API 就能用模块格式提供,不再必须做成全局变量或者
navigator
对象的属性。 - 不再需要对象作为命名空间(比如
Math
对象),未来这些功能可以通过模块提供。
import()
import
命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行
import
和export
命令只能在模块的顶层
这样的设计,固然有利于编译器提高效率,但也导致无法在运行时加载模块。在语法上,条件加载就不可能实现。
import()
函数,支持动态加载模块。
运行时执行:也就是说,什么时候运行到这一句,就会加载指定的模块。
import()
返回一个 Promise 对象。
js
import('xxx')
.then(module => {module.default})
适用场合
- 按需加载
- 条件加载
js
if(a) {import('xxx').then(({a,b,c}) =>{})}
- 动态的模块加载
js
import(f())
import()
也可以用在 async 函数之中。
严格模式
ES6 的模块自动采用严格模式
export 命令
export
命令规定的是对外的接口,必须与模块内部的变量建立一一对应关系。
js
// 报错
var m = 1;
export m;
因为没有提供对外的接口。直接输出 1。1
只是一个值,不是接口。
export
命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错
因为处于条件代码块之中,就没法做静态优化了
import 命令
import
命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。
import
命令具有提升效果,会提升到整个模块的头部,首先执行。
js
foo();
import { foo } from 'my_module';
上面的代码不会报错,因为import
的执行早于foo
的调用。这种行为的本质是,import
命令是编译阶段执行的,在代码运行之前。
由于import
是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。
js
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
import
语句会执行所加载的模块,因此可以有下面的写法。
js
import 'lodash';
多次重复执行同一句import
语句,那么只会执行一次,而不会执行多次。
js
import 'lodash';
import 'lodash';
js
import { foo } from 'my_module';
import { bar } from 'my_module';
// 等同于
import { foo, bar } from 'my_module';
import
在静态解析阶段执行,所以它是一个模块之中最早执行的。
js
require('core-js/modules/es6.symbol');
require('core-js/modules/es6.promise');
import React from 'React';
模块的整体加载
js
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
export default 命令
export default
命令其实只是输出一个叫做default
的变量,所以它后面不能跟变量声明语句。
js
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
// 正确
export default 42;
// 报错
export 42;
因为export default
命令的本质是将后面的值,赋给default
变量,所以可以直接将一个值写在export default
之后。
如果想在一条import
语句中,同时输入默认方法和其他接口,可以写成下面这样。
js
import _, { each, forEach } from 'lodash';
export 与 import 的复合写法
js
export { foo, bar } from 'my_module';
在一个模块之中,先输入后输出同一个模块
foo
和bar
实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用foo
和bar
。
Module 的加载实现
没看