ES6和CommonJS都有本身的一套处理模块化代码的措施,即JS文件之间的相互引用。node
为了方便两种方式的测试,使用nodejs的环境进行测试缓存
使用require来引入其余模块的代码,使用module.exports来引出闭包
// exportDemo.js
count = 1;
module.exports.count = count;
module.exports.Hello = function() {
var name;
this.setName = function(newName) {
name = newName;
}
this.sayHello = function() {
console.log("hello Mr." + name);
}
this.getId = function() {
return count++
}
}
复制代码
// requireDemo.js
var {Hello} = require("./exportDemo")
var hello = new Hello();
hello.setName("Blank");
hello.sayHello();
复制代码
在终端执行node requireDemo.js
,返回结果为'hello Mr.Blank'模块化
导出的Hello函数是原函数的一次拷贝,修改Hello函数的属性值不会对其余require的地方形成影响函数
var { Hello, count } = require('./exportDemo')
var hello = new Hello();
// 让count自增
console.log(hello.getId());
console.log(hello.getId());
// 发现获取的count仍是原值
console.log(count)
// 真正的count实际上是已经改了的
var newHello = new Hello();
console.log(newHello.getId())
var { Hello: newHello, count: newCount } = require('./exportDemo')
console.log(newCount, 'newCount');
// 再次require,取得的newHello和以前require的Hello指向同一个拷贝
console.log(newHello === Hello)
复制代码
nodejs默认是不支持ES6的模块处理方案的。测试
可是在8.5.0以后,ES6代码的文件格式定为mjs后,可以使用node --experimental-modules xxx.mjs
运行。ui
// exportDemo.mjs
export let a = 1;
复制代码
// importDemo.mjs
import {a} from './exportDemo.mjs'
console.log(a)
复制代码
CommonJS 模块输出的是一个值的拷贝(已在上一章验证),ES6 模块输出的是值的引用。this
// exportDemo.mjs
export let counter = 1;
export function incCounter() {
counter ++;
}
复制代码
// importDemo.mjs
import { counter, incCounter } from './exportDemo.mjs'
incCounter();
console.log(counter) // 打印结果为2,而不是初始值的1
复制代码
CommonJS模块是运行时加载,ES6模块是编译时输出接口spa
Nodejs此类的运行环境会在一个闭包中运行CommonJS模块代码3d
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
复制代码
ES6 模块不会缓存运行结果,而是动态地去被加载的模块取值,而且变量老是绑定其所在的模块。
// exportDemo.mjs
export let a = 1;
export const b = 2;
export let obj = {};
// importDemo.mjs
import { a, b } from './exportDemo.mjs'
console.log(a, b)
a = 1 // 报错,TypeError: Assignment to constant variable,export出来的值是一个只读引用
obj.x = 1 // 能够改变属性值
复制代码
在ES6模块中咱们更多地要去考虑语法的问题
有时候咱们会在代码发现export default obj
的用法,那么这个default是用来干吗的?
default是ES6引入的与export配套使用的关键字,用来给匿名对象、匿名函数设置默认的名字用的
export出来的值必需要有一个命名,不然从语法层次便会报错
让咱们看一下如下几个会报错的错误例子
export匿名对象
export { x: 1 } // 报错,SyntaxError:Unexpected token,这是一个编译阶段的错误
// 正确写法
export default { x: 1 }
复制代码
export匿名函数
export function() {} // 报错,SyntaxError: Unexpected token (
// 正确写法
export default function() {}
复制代码
在复杂的模块中,可能会出现模块间的互相引用。
// a.js
exports.loaded = false;
var b = require('./b.js')
console.log("b in a is " + JSON.stringify(b))
exports.loaded = true;
console.log("a complete")
复制代码
// b.js
exports.loaded = false;
var a = require('./a.js')
console.log("a in b is " + JSON.stringify(a))
exports.loaded = true;
console.log("b complete")
复制代码
// main.js
var a = require('./a.js')
var b = require('./b.js')
console.log("a in main is" + JSON.stringify(a))
console.log("b in main is" + JSON.stringify(b))
复制代码
执行指令nodejs main.js
时序图下的执行步骤分解图以下所示:
// a.mjs
import { bar } from './b.mjs'
console.log(bar);
export let foo = 'foo from a.mjs'
复制代码
// b.mjs
import { foo } from './a.mjs'
console.log(foo)
export let bar = 'bar from b.mjs'
复制代码
// main.mjs
import { foo } from './a.mjs'
import { bar } from './b.mjs'
复制代码
node main.mjs
时序图下的执行步骤分解图以下所示:
ES6的循环引用要特别注意变量是否已被声明,若未被声明的块级做用域变量被其余模块引用时,会报错。
改进方案:循环引用中尽可能去export能够提早肯定的值(例如函数),其实咱们老是但愿去引用模块执行彻底后最终肯定的变量。
// a.mjs
import { bar } from './b.mjs'
console.log(bar());
export function foo() {
return 'foo from a.mjs'
}
复制代码
// b.mjs
import { foo } from './a.mjs'
console.log(foo());
export function bar() {
return 'bar from b.mjs'
}
复制代码
// main.mjs
import { foo } from './a.mjs'
import { bar } from './b.mjs'
复制代码
node main.mjs
返回结果:
foo from a.mjs
bar from b.mjs
复制代码
时序图下的执行步骤分解图以下所示: