CommonJS 规范
定义的模块分为3部分: require(引用), exports(导出), module(本身)。规定一个文件就是一个模块,文件中定义的变量、函数、类,都是私有的,其他文件是不可见。加载模块是同步的,所以只有加载完成才能执行后面的操作。但如果是浏览器环境,要从服务器加载模块,这是就必须采用异步模式。所以就有了 AMD CMD 等解决方案。1
2
3
4
5
6
7
8// a.js
var a = {
"a":a,
"b":b
}
module.export = a //模块导出
// b.js
var b = require('./a.js') //模块引入
ES6 Module
使用 export 定义了对外接口,其他文件通过 import 加载这个模块,export 命令可以出现在模块的任何位置,只要处于模块顶层就可以。
import 命令具有提升效果,会提升到整个模块的头部,首先执行。由于 import 是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。1
2
3
4export { foo, bar } from 'my_module';
export { foo as myFoo } from 'my_module';// 接口改名
export { es6 as default } from './someModule';
export { default as es6 } from './someModule';
ES6默认就是严格模式,严格模式主要有以下限制:
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用with语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量 delete prop,会报错,只能删除属性 delete global[prop]
- eval 不会在它的外层作用域引入变量
- eval 和 arguments 不能被重新赋值
- arguments 不会自动反映函数参数的变化
- 不能使用 arguments.callee
- 不能使用 arguments.caller
- 禁止 this 指向全局对象
- 不能使用 fn.caller和fn.arguments 获取函数调用的堆栈
- 增加了保留字(比如protected、static和interface)
Module 的加载实现1
2
3<script type="module" src="./foo.js"></script>
<!-- 等同于 -->
<script type="module" src="./foo.js" defer></script>
- 代码是在模块作用域之中运行,而不是在全局作用域运行。模块内部的顶层变量,外部不可见。
- 模块脚本自动采用严格模式,不管有没有声明 use strict。
- 模块之中,可以使用 import命令 加载其他模块(.js后缀不可省略,需要提供绝对 URL 或相对 URL),也可以使用 export 命令输出对外接口。
- 模块之中,顶层的 this 关键字返回 undefined,而不是指向 window。也就是说,在模块顶层使用 this 关键字,是无意义的。
- 同一个模块如果加载多次,将只执行一次。
ES6 模块与 CommonJS 模块的差异以及如何处理”循环加载”:
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
- ES6 模块是动态引用,如果使用 import 从一个模块加载变量(即import foo from ‘foo’),那些变量不会被缓存,而是成为一个指向被加载模块的引用,需要开发者自己保证,真正取值的时候能够取到值。CommonJS 模块代码在require的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
AMD 规范
异步加载模块,适用define方法定义模块,运行时核心思想是「Early Executing」,也就是提前执行依赖。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33// define
// id: 模块标识,可省。
// dependencies: 所依赖的模块,可省。
// factory: 模块的实现,或者一个JavaScript对象。
//a.js 只有factory
define(function() {
return {
mix: function(source, target) {
...
}
};
});
//b.js 依赖a.js
define(['a'], function(a) {
return {
show: function() {
...
}
}
});
//c.js 依赖a.js b.js
define(['a', 'b'], function(a, b) {
....
});
//d.js 对象模块
define({
data1: [],
data2: []
});
AMD规范允许输出模块兼容CommonJS规范,这时define方法如下:1
2
3
4
5
6
7define(function (require, exports, module) {
var reqModule = require("./a.js");
requModule.mix();
exports.asplode = function () {
...
}
});
CMD 规范
CMD 和 AMD 的区别:对于依赖的模块AMD是提前执行,CMD是延迟执行。不过RequireJS从2.0开始,也改成可以延迟执行(根据写法不同,处理方式不通过),CMD依赖就近,AMD依赖前置。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18//AMD写法
define(['./a','./b'], function (a, b) {
//依赖一开始就写好
a.mix();
b.show();
});
//CMD写法
define(function (requie, exports, module) {
//依赖可以就近书写
var a = require('./a');
a.mix();
if (...) {
var b = requie('./b');
b.show();
}
});
UMD 规范
应用 UMD 规范的 js 文件其实就是一个立即执行函数。函数有两个参数,第一个参数是当前运行时环境,第二个参数是模块的定义体。在执行UMD规范时,会优先判断是当前环境是否支持AMD环境,然后再检验是否支持CommonJS环境,否则认为当前环境为浏览器环境( window )。
1 | (function (root, factory) { |
Reference:
https://segmentfault.com/a/1190000006232697
http://www.cnblogs.com/imwtr/p/4666181.html
http://caibaojian.com/es6/module.html
http://www.cnblogs.com/vs1435/p/6553139.html