一.CommonJS模块与ES6模块的区别
- CommonJS模块输出的是一个值的拷贝,ES6模块输出的是值的引用。
- CommonJS模块是运行时加载,ES6模块是编译时输出接口。
- CommonJS模块的require()是同步加载模块,ES6模块的import命令是异步加载,有一个独立的模块依赖的解析阶段。
第二个区别是因为CommonJS加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
第一个区别
CommonJS模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。例如:
上面代码输出内部变量counter和改写这个变量的内部方法incCounter。然后,在main.js里面加载这个模块。
上面代码说明,lib.js模块加载之后,它的内部变化就影响不到输出的mod.counter了。这是因为mod.counter是一个原始类型的值,会被缓存。除非写成一个函数,才能得到内部的值。例如:
上面代码中,输出的counter属性实际上是一个取值器函数。现在再执行main.js,就可以正确读取内部变量counter的变动了。
ES6模块的运行机制与CommonJS不一样。JS引擎对脚本静态分析的时候,遇到模块加载命令import,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。例如:
上面代码说明,ES6模块输入的变量counter是活的,完全反应其所在模块lib.js内部的变化。
再说一个出现在export中的例子:
上面代码表明,ES6模块不会缓存运行结果,而是动态地去被加载模块取值,并且变量总是绑定其所在的模块。
上面代码中,main.js从lib.js输入变量obj,可以对obj添加属性,但是不能重新赋值。因为变量的地址是只读的。
最后,export通过接口,输出的是同一个值。不同的脚本加载这个接口,得到的都是同样的实例。例如:
上面的脚本mod.js,输出的是一个c的实例。不同的脚本加载这个模块,得到的都是同一个实例。
这就证明了x.js和y.js加载的都是c的同一个实例。