简单来说 AMD CMD都是属于ES5语法 CommonJS属于node后端
1.AMD 是RequireJS 在推广过程中对模块定义的规范化产出(AMD 是提前执行),不过在requireJS的2.0开始也改成了延迟执行(写法不同,处理方式不同)
AMD 即Asynchronous Module Definition,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,服务器端的规范是CommonJS。模块将被异步加载,模块加载不影响后面语句的运行。所有依赖某些模块的语句均放置在回调函数中。
AMD规范只定义了一个函数 define,它是全局变量。函数为:define(id,dependencies,factory);
define函数里面的:
id (模块名)
是唯一确定的值,不可重复,可选。
模块名是用正斜杠分割的有意义的字符串,单词为驼峰命名法(或者 " . " " , " " .. " ),不允许文件扩展名
模块名可以是相对的或者顶级的, 如果首字符为 " . " " .. " 则为相对的模块名
dependencies(依赖)
是一个当前模块依赖,模块定义的模板标识的数字字面量。
当然依赖参数也是可选的 它默认为 [ 'require' , 'exports' , ' module ' ] , 如果factory的长度属性小于3,加载器会选择以函数的长度属性指定的参数个数调用factory方法
factory(工厂方法)
模块初始化要是行的函数或者对象。依赖模块必须根据模块的factor优先级执行,并且执行的结果应该按照依赖数组组的位置顺序传入(定义模块中)的工厂方法。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值。
创建一个id(模板名)为“alpha”的模块,使用了名为:require, exports, beta 的模块。
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb=function() {
return beta.verb();
//Or:returnrequire("beta").verb();
}
});
一个返回对象的匿名模块:
define(["alpha"], function (alpha) {
return {
verb:function(){
return alpha.verb() +2;
}
};
});
一个没有依赖性的模块可以直接定义对象:
define({
add:function(x, y){
return x + y;
}
});
一个使用了简单CommonJS转换的模块定义:
define(function (require, exports, module) {
var a =require('a'),
b =require('b');
exports.action=function () {};
});
实现AMD的库有RequireJS 、curl 、Dojo 、Nodules
等。
2.CMD 是 SeaJS 在推广过程中对模块定义的规范化产出。(CMD 是延迟执行)
CMD 推崇依赖就近,AMD 推崇依赖前置。
// CMD
define(function(require, exports, module) {
// 依赖可以就近书写
var a = require('./a')
a.doSomething()
// 此处略去 1000 行
var b = require('./b')
b.doSomething()
})
// AMD 默认推荐的是:
define(['./a', './b'], function(a, b) {
// 依赖必须一开始就写好
a.doSomething()
// 此处略去 1000 行
b.doSomething()
...
})
AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹。
3.CommonJS模块输出的是一个值的拷贝,而且是在运行时加载
commonJS {
module.exports = ... : '只能输出一个,且后面的会覆盖上面的' ,
exports. ... : ' 可以输出多个',
运行阶段确定接口,运行时才会加载模块,
模块是对象,加载的是该对象,
加载的是整个模块,即将所有的接口全部加载进来,
输出是值的拷贝,即原来模块中的值改变不会影响已经加载的该值,
this 指向当前模块
}
commonJS是使用require来引入其他模块的代码,使用module.exports来引出:
//exportsDemo.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++
}
}
用require引入上面的模块
// requireDemo.js
var {Hello} = require("./demo")
var hello = new Hello();
hello.setName("Blank");
hello.sayHello();
4.ES6模块是编译时输出接口,也是输出的值的引用
ES6模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。
ES6 模块不会缓存运行结果,而是动态地去被加载的模块取值,并且变量总是绑定其所在的模块。
ES6的循环引用要特别注意变量是否已被声明,若未被声明的块级作用域变量被其他模块引用时,会报错。
es6 {
export : '可以输出多个,输出方式为 {}' ,
export default : ' 只能输出一个 ,可以与export 同时输出,但是不建议这么做',
解析阶段确定对外输出的接口,解析阶段生成接口,
模块不是对象,加载的不是对象,
可以单独加载其中的某个接口(方法),
静态分析,动态引用,输出的是值的引用,值改变,引用也改变,即原来模块中的值改变则该加载的值也改变,
this 指向undefined
}
es6模块中的值属于【动态只读引用】。只说明一下复杂数据类型。
对于只读来说,即不允许修改引入变量的值,import的变量是只读的,不论是基本数据类型还是复杂数据类型。当模块遇到import命令时,就会生成一个只读引用。等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。
对于动态来说,原始值发生变化,import加载的值也会发生变化。不论是基本数据类型还是复杂数据类型。
// b.js
export let counter = {
count: 1
}
setTimeout(() => {
console.log('b.js-1', counter.count) }
, 1000
)
// a.js
import { counter } from './b.js'
counter = {}
console.log('a.js-1', counter)
虽然不能将counter重新赋值一个新的对象,但是可以给对象添加属性和方法。此时不会报错。这种行为类型与关键字const的用法。
// a.js
import { counter } from './b.js'
counter.count++
console.log(counter)
循环加载时,ES6模块是动态引用。只要两个模块之间存在某个引用,代码就能够执行。
// b.js
import {foo} from './a.js';
export function bar() {
console.log('bar');
if (Math.random() > 0.5) {
foo();
}
}
// a.js i
mport {bar} from './b.js';
export function foo() {
console.log('foo');
bar();
console.log('执行完毕');
}
foo();