CommonJS、AMD、CMD、ES6的区别
其实,CommonJS规范的提出,主要是为了弥补JavaScript没有标准的缺陷,已达到像Python、Ruby和Java那样具备开发大型应用的基础能力,而不是停留在开发浏览器端小脚本程序的阶段。模块规范主要分为三部分:模块引用、模块定义、模块标识。
同时,模块规范很好地解决变量污染问题,每个模块具有独立空间,互不干扰,命名空间等方案与之相比相形见绌。模块规范支持引入和导出功能,这样可以顺畅地连接各个模块,实现彼此间的依赖关系。
AMD、CMD、ES6规范则继CommonJS规范之后逐渐诞生,因为CommonJS规范更适合服务器端,而其他三种则更好的适用于浏览器端。
正文开始-------
一、JavaScript模块发展历程
function m1(){
//...
}
function m2(){
//...
}
这种做法的缺点:”污染“了全局变量,无法保证不与其他模块发生命名冲突,而且模块之间并看不出直接关系。
var module = new Object({
name:'xiaoming',
fn1:function(){
//...
},
fn2:function(){
//...
}
})
当我们使用时候则直接调用对象的属性:module.fn1
、module.name
…
但是这种写法会暴露所有模块成员,外部可以直接改写内部变量:module.name = 'honghong'
…
在ES6(ECMAScript 6)之前,还没有一套官方的规范,通行的JavaScript模块规范化有两种:CommonJS和AMD
注:ECMAScript 和 JavaScript 的关系:前者是后者的语法规格,后者是前者的一种实现
2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。
这标志”Javascript模块化编程”正式诞生。前端的复杂程度有限,没有模块也是可以的,但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。
node编程中最重要的思想之一就是模块,而正是这个思想,让JavaScript的大规模工程成为可能。模块化编程在js界流行,也是基于此,随后在浏览器端,requirejs和seajs之类的工具包也出现了,可以说在对应规范下,require统治了ES6之前的所有模块化编程,即使现在,在ES6 module被完全实现之前,还是这样。
在CommonJS中,暴露模块使用module.exports和exports,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。
var math = require("math")
//调用
math.addNum(1,5) // 6
因为CommonJS的推动,才有了后面的AMD、CMD也采用require引用模块的风格。
由于CommonJS奠定了服务器模块规范,大家便开始考虑客户端模块,而且想两者可以兼容,一个模块可以同时在服务器和浏览器运行。
但是CommonJS是同步加载模块,服务器所有模块都存放在本地,硬盘读取时间很快,但是对于浏览器来说,等待时间则取决于网速的快慢,如果时间过长,浏览器则处于”假死“状态。例如如上代码,当我们调用`math.addNum(1,5),浏览器只能等待math.js加载完成后才能进行计算,所以浏览器端模块化不能使用同步加载,需用异步加载取代之,这也就是AMD规范诞生的背景。
AMD是”Asynchronous Module Definition”的缩写,意思就是”异步模块定义”。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
define(id?,dependencies?,factory)
//id:字符串,模块名称(可选)
//dependencies:要载入的依赖模块(可选)
//factiry:工厂方法:返回一个模块函数
当然,如果一个模块不依赖于其他模块,那么可以直接定义在define()中
//main.js
define(function (){
var add = function(a,b){
return a+b;
}
return add
})
如果这个模块依赖于其他模块,那它第一个函数需要是一个数组,指明它以来的模块:
//main.js
define(['module1'],function (module1){
var foo = function(){
module1.doSomething()
}
return foo
})
当我们require()模块时,就会预先加载module1模块
require([module],callback())
require(['math'], function (math) {
math.add(2, 3);
});
math.add()和math模块异步加载,不会让浏览器发生假死现象,相对CommonJS,它更适合于浏览器。
CMD (Common Module Definition), 是seajs推崇的规范,CMD则是依赖就近,用的时候再require。它写起来是这样的:
define(function(require, exports, module) {
var clock = require('clock');
clock.start();
});
CMD与AMD一样,也是采用特定的define()函数来定义,用require方式来引用模块
define(id?, dependencies?, factory)
//id:字符串,模块名称(可选)
//dependencies: 是我们要载入的依赖模块(可选),使用相对路径。,注意是数组格式
//factory: 工厂方法,返回一个模块函数
define('hello', ['jquery'], function(require, exports, module) {
// 模块代码
});
如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。
define(function(require, exports, module) {
// 模块代码
});
Es6标准发布后,成为module标准:使用export指令导出接口,import引入模块,但是在node模块中,我们依然使用CommonJS规范,(require引入模块,module.exports导出接口)
export { name1, name2, …, nameN };
export { variable1 as name1, variable2 as name2, …, nameN };
export let name1, name2, …, nameN; // also var
export let name1 = …, name2 = …, …, nameN; // also var, const
export default expression;
export default function (…) { … } // also class, function*
export default function name1(…) { … } // also class, function*
export { name1 as default, … };
export * from …;
export { name1, name2, …, nameN } from …;
export { import1 as name1, import2 as name2, …, nameN } from...
export { myFunction }; // 导出一个已定义的函数
export const foo = Math.sqrt(2); // 导出一个常量
export * from 'article
var name = 'IT笔录';
var domain = 'http://itbilu.com';
export {name, domain}; // 相当于导出
{name:name,domain:domain}
模块导出时,我们可以使用as关键字对导出成员进行重命名:
var name = 'IT笔录';
var domain = 'http://itbilu.com';
export {name as siteName, domain};
注意,下面的语法有严重错误的情况:
// 错误演示
export 1; // 绝对不可以
var a = 100;
export a;
export a 看上去没有问题,但是a是一个数字,无法进行解构,所以必须写成export {a}
形式,即使a赋予的是一个function,也不允许
export default function(){}
export default class(){}
注意 以下两种形式导出等价
const Aa = 'javaScript'
export default Aa
export {Aa as default}
定义
//module1.js
export function fn1(a,b){
return a+b
}
const num = 123
export {num}
引用:
import {fn1,num} from 'module1'
//module2.js
export default function(){
console.log('默认导出')
}
引用:
import fn2 from 'module2'
fn2(); // 默认导出
示例:
import defaultMember from "module-name";
import * as name from "module-name";
import { member } from "module-name";
import { member as alias } from "module-name";
import { member1 , member2 } from "module-name";
import { member1 , member2 as alias2 , [...] } from "module-name";
import defaultMember, { member [ , [...] ] } from "module-name";
import defaultMember, * as name from "module-name";
import "module-name";
到此结束一段落,仅为学习笔记,菜鸟一枚,原理性知识理解不够充分,很多内容都是借鉴而来,如有错误请直接指出,共同进步。
加油吧,打工人!