// ES6模块,实质是从fs模块加载3个方法,其他方法不加载
import { stat, exists, readFile } from 'fs';
<script type="module" src="XX.js"></script>
使用
// 方法一:直接export
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
// 方法二: 使用{}export
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export {firstName, lastName, year};
// 对外输出了multiply
export function multiply(x, y) {
return x * y;
};
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
备注
// 报错
export 1;
// 报错
var m = 1;
export m;
// 正确写法
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}
备注
import { lastName as surname } from './profile';
export { es6 as default } from './someModule';
// 等同于
import { es6 } from './someModule';
export default es6;
// circle.js,导出方法
export function area(radius) {
return Math.PI * radius * radius;
}
export function circumference(radius) {
return 2 * Math.PI * radius;
}
// file2.js,加载方法
// 指定加载
import { area, circumference } from './circle';
console.log('圆面积:' + area(4));
console.log('圆周长:' + circumference(14));
// 整体加载
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
// file.js 默认输出一个函数
export default function(){
console.log('default export');
}
// 加载默认export文件
import customName from './file';
customName(); // 'foo'
// 输出
export default function defaultFn() {
// ...
}
// 输入
import defaultFn from 'defaultFn';
// 输出
export function defaultFn() {
// ...
};
// 输入
import {defaultFn} from 'defaultFn';
function add(x, y) {
return x * y;
}
export {add as default}; // 等同于 export default add;
// app.js
import { default as xxx } from 'modules'; // 等同于 import xxx from 'modules';
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
import customName, { otherMethod } from './export-default';
export default 42;
// MyClass.js
export default class { ... }
// main.js
import MyClass from 'MyClass';
let o = new MyClass();
// circleplus.js(假设circleplus模块继承circle模块)
// 方法一
export * from 'circle'; // 输出继承来的所有circle模块的属性和方法,忽略default方法
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// 方法二
export { area as circleArea } from 'circle'; // 将circle的属性或方法改名后再输出
// 加载上面circleplus模块
// main.js
import * as math from 'circleplus'; // 加载所有属性和方法
import exp from 'circleplus'; // 加载默认方法为exp
console.log(exp(math.e));
ES6模块是动态引用,并且不会缓存值,模块里面的变量绑定其所在的模块。
// lib.js
export let counter = 3;
export function incCounter() {
counter++;
}
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 3
// lib.js
var counter = 3;
function incCounter(){
counter++;
};
modle.exports = {
get counter(){
return counter;
},
incCounter: incCounter
};
// main.js
var mod = require('./lib');
console.log(mod.counter); // 3
mod.incCounter();
console.log(mod.counter); // 4
// 实例一
// lib.js
var counter = 3;
function incCounter(){
counter++;
};
// main.js
import { counter, incCounter } from './lib';
console.log(counter); // 3
incCounter();
console.log(counter); // 4
// 实例二
// m1.js
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
// m2.js
import {foo} from './m1.js';
console.log(foo);
setTimeout(() => console.log(foo), 500);
// lib.js
export let obj = {};
// main.js
import { obj } from './lib';
obj.prop = 123; // OK
obj = {}; // TypeError:babel-compile都不会通过
// mod.js
function C() {
this.sum = 0;
this.add = function () {
this.sum += 1;
};
this.show = function () {
console.log(this.sum);
};
}
export let c = new C();
// x.js
import {c} from './mod';
c.add();
// y.js
import {c} from './mod';
c.show();
// main.js
import './x';
import './y';
"循环加载"表示存在强耦合,如果处理不好,还可能导致递归加载,使得程序无法执行,因此应该避免出现。
目前最常见的两种模块格式CommonJS和ES6,处理"循环加载"的方法是不一样的,返回的结果也不一样。
CommonJS的一个模块,就是一个脚本文件。require命令第一次加载该脚本,就会执行整个脚本,然后在内存生成一个对象。之后即使再次执行require命令,也不会再次执行该模块,而是到缓存之中取值。
// Node内部加载模块后生成的一个对象
{
id: '...', // 加载模块名
exports: { ... }, // 模块输出的各个接口
loaded: true, // 布尔值,表示该模块的脚本是否执行完毕
...
}
CommonJS模块的重要特性是加载时执行,即脚本代码在require的时候,就会全部执行。一旦出现某个模块被"循环加载",就只输出已经执行的部分,未执行的部分不会输出。
// a.js
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
//b.js
console.log('b starting');
exports.done = false;
const a = require('./a.js'); //此时只执行a.js的第一行,发生循环加载,系统会去a.js模块对应对象的exports取值,但还没执行完,因此只能取部分值(一旦require就会生成对象,然后一步步执行,同时往对象加值)。
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
// main.js
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js'); // 已经加载,只会从缓存中取值
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
// 执行结果
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
ES6模块是动态引用,如果使用import从一个模块加载变量,那些变量不会被缓存,而是成为一个指向被加载模块的引用
// a.js如下
import {bar} from './b.js';
console.log('a.js');
console.log(bar);
export let foo = 'foo';
// b.js
import {foo} from './a.js';
console.log('b.js');
console.log(foo);
export let bar = 'bar';
// 执行结果
b.js
undefined
a.js
bar
// a.js
import {bar} from './b.js'; // 建立引用,从b.js引用‘bar’
export function foo() {
console.log('foo');
bar(); // 到b.js执行bar
console.log('执行完毕');
}
foo(); // 执行时,引用都已完全建立
// b.js
import {foo} from './a.js'; // 建立引用,从a.js引用foo
export function bar() {
console.log('bar');
if (Math.random() > 0.5) { // 递归执行foo,一旦随机数小于等于0.5就停止执行
foo();
}
}
// 执行结果
// Math.random() <= 0.5
foo
bar
执行完毕
// Math.random() > 0.5
foo
bar
foo
bar
执行完毕
执行完毕
const声明的常量只在当前代码块有效。如果想设置跨模块的常量(即跨多个文件),可讲常量设置为一个单独的模块,可用以下代码实现
// constants.js 模块
export const A = 1;
export const B = 3;
export const C = 4;
// test1.js 模块
import * as constants from './constants';
console.log(constants.A); // 1
console.log(constants.B); // 3
// test2.js 模块
import {A, B} from './constants';
console.log(A); // 1
console.log(B); // 3
浏览器目前还不支持ES6模块,现在要使用需要将其转换为ES5写法,有三种方法:
ES6 module transpiler
使用
// 其中file1.js和file2.js是需要转换的es6文件
compile-modules convert file1.js file2.js
// -o指定转码后的文件名,如果不指定,则默认在命令行输出转换后代码。out1.js如果不存在则会自动生成
$ compile-modules convert file1.js file2.js -o out1.js out2.js
// a.js
export function foo() {
console.log('foo');
}
// test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<!--加载Google的traceur转码器-->
<script src="http://google.github.io/traceur-compiler/bin/traceur.js" type="text/javascript"></script>
<!--加载system.js文件->
<script src="system.js"></script>
<script>
// import a.js
System.import('a.js').then(function(m) {
console.log(m.foo());
});
</script>
</head>
<body>
</body>
</html>