不同于Nodejs的动态加载, ES6的模块技术确是静态的
导出什么东西?
我们首先思考一下, 我们在使用export去导出什么东西。我们导出的是名字(name)和值(value)。毕竟关键字, 那些加减乘除符号和一些括号符号之外, 名字和值就是你代码出现且有用的东西了。
//基础声明的name
export <let/const/var> x;
export function x(){};
export class x;
export {x, y, z ...};
//重命名
export {x as y, ...};
export {x as default, ...};
//导出其它模块的名字
export ... from ...;
关于名字很好处理, 将导出的名字放在一个符号表中, 当导入的时候从表中读取就可以了。
那么值(value)如何去处理, 值压根没有名字, 何谈导出,所以为了这一点JavaScript为导出的值去设计了一个特殊的名字叫做default。这样导出值就有了一个默认的名字叫做default, 于是你就可以导出这个值了。
于是你可以写出下面这种代码:
export default expression;
export default 2;
export default "hello, world";
//对象字面量也是一个值
export default {};
//匿名函数也是一个值
export default function() {};
导出语句与导入语句的处理逻辑
我们导出一个名字之后, 还需要使用它 比如
模块1
export let a = 1;
模块2
import a from "./xxx.js"
console.log(a + 1);
由于其他模块需要使用, 我们就需要为导出的名字绑定一个值, 所以export的处理逻辑为
- 在导出变量的文件形成一个模块表, 登记名字
- 为名字绑定一个值, 这是JavaScript执行期间的东西
可以使用JavaScript简单模拟一下:
let table = [];
table.push({name:a, value: 1});
对应的模块2的import处理逻辑为
- 添加对该模块的一个依赖, 这样我们就可以根据import形成模块依赖树, 找到跟模块, 然后加载了.
- 在当前模块环境(scope)声明一个名字
所以总结export/import就是:
- 根据export形成一个名字表
- 根据import形成一个依赖
所以说ES6的模块系统export/import完全是静态的, 因为根本就没有出现像a+1这样需要执行去代码.但是到了执行阶段就要做下面的事情:
- 找到静态装载阶段的模块树, 并且遍历
- 执行最顶层的代码
- 为名字绑定值, 包括为导出值绑定default这个特殊的名字
- 使用名字