AMD , CMD , CommonJs 和 ES6 对比

1.CommonJS

1.1 基本概念

  1. 为JS的表现来制定规范,因为js没有模块的功能所以CommonJS应运而生,它希望js可以在任何地方运行,不只是浏览器中。
  2. 同步
  3. CommonJS规范 通过model.exports定义的,在前端浏览器中并不支持
  4. NodeJS是CommonJS规范的实现,webpack 也是以CommonJS的形式来书写的。
  5. CommonJS定义的模块分为: 模块引用(require) ; 模块定义(exports) ; 模块标识(module)

require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。

var foo = require('foo.js');
var count = 1;
var plusCount = () => {
  foo.add(count);
};
module.exports = {
  count,
  plusCount
};

浏览器不兼容CommonJS的根本原因,也正是在于缺少四个Node.js环境的变量。

  • module
  • exports
  • require
  • global

1.2 特点

  • 对于基本数据类型,属于复制。即会被模块缓存。同时,在另一个模块可以对该模块输出的变量重新赋值。
  • 对于复杂数据类型,属于浅拷贝。由于两个模块引用的对象指向同一个内存空间,因此对该模块的值做修改时会影响另一个模块。
// b.js
let num = 1;
let obj = {
    name: 'hcd'
};
setTimeout(() => {
  console.log('b.js-num:', num);
  console.log('b.js-name:', obj.name);
}, 1000)
module.exports = {
  num,
  obj
}



// a.js
let mod = require('./b.js')
mod.num = 2;
mod.obj.name = 'newName'
console.log('a.js-num:', mod.num);
console.log('a.js-name:', mod.obj.name);



//运行node
node a.js
a.js-num:  2
a.js-name: newName
b.js-num:  1         // 1秒后
b.js-name: newName  // 1秒后
  • 当使用require命令加载某个模块时,就会运行整个模块的代码。

  • 当使用require命令加载同一个模块时,不会再执行该模块,而是取到缓存之中的值。也就是说,CommonJS模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存。

  • 循环加载时,属于加载时执行。即脚本代码在require的时候,就会全部执行。一旦出现某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。
// a.js
exports.done = false
let b = require('./b.js')
console.log('a.js-1', b.done)
exports.done = true
console.log('a.js-2', '执行完毕')




// b.js
exports.done = false
let a = require('./a.js')
console.log('b.js-1', a.done)
exports.done = true
console.log('b.js-2', '执行完毕')




// c.js
let a = require('./a.js')
let b = require('./b.js')

console.log('c.js-1', '执行完毕', a.done, b.done)



运行node c.js
b.js-1 false
b.js-2 执行完毕
a.js-1 true
a.js-2 执行完毕
c.js-1 执行完毕 true true

说明:

  1. 在Node.js中执行c模块。此时遇到require关键字,执行a.js中所有代码。
  2. 在a模块中exports之后,通过require引入了b模块,执行b模块的代码。
  3. 在b模块中exports之后,又require引入了a模块,此时执行a模块的代码。
  4. 因为 某个模块被”循环加载”,就只输出已经执行的部分,还未执行的部分不会输出。a模块只执行exports.done =
    false这条语句。
  5. 回到b模块,打印b.js-1, exports, b.js-2。b模块执行完毕
  6. 回到a模块,接着打印a.js-1, exports, b.js-2。a模块执行完毕
  7. 回到c模块,接着执行require,需要引入b模块。由于在a模块中已经引入过了,所以不会再执行该模块,而是取到缓存之中的值,直接就可以输出值了。 结束。

2.AMD

2.1诞生背景

基于commonJS规范的nodeJS出来以后,服务端的模块概念已经形成,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。因为会有一个很大的问题:

 var math = require('math');

 math.add(2, 3);

第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。您会注意到 require 是同步的

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。

因此,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous)。这就是AMD规范诞生的背景。

CommonJS是主要为了JS在后端的表现制定的,他是不适合前端的,AMD(异步模块定义)出现了,它就主要为前端JS的表现制定规范。

2.1AMD

AMD (异步模块定义Asynchronous Module Definition) :是require.js在推广过程中对模块定义的规范化产出

  • AMD:

    1. 模块本身和模块之间的引用可以被异步的加载,是一个概念
    2. 先引入的模块,后使用的引用模块的方法,所以我们称之为依赖前置
  • AMD优点:

    1. 包括异步的调用和本身的高扩展性,
    2. 它实现了解耦,模块在代码中也可通过识别号进行查找。
  • require.js

    1. 是对AMD这个概念的实现,
  • require.js的诞生,就是为了解决这两个问题:

    1. 实现js文件的异步加载,避免网页失去响应;

    2. 管理模块之间的依赖性,便于代码的编写和维护。

  • 官网:

    http://www.requirejs.cn/

define(['./package/lib.js'], function(lib) {
      function say(){
           lib.log('this is fn');
       }
       return {
           say:say
       }; 
});

说明:

  1. ./package/lib.js使我们依赖的模块,回调函数中参数lib表示的是引入的模块的所有方法和属性,我们可以调用

  2. 后来我们又在当前模块定义了say方法,并return输出

  3. 可以看到我们是先引入的模块,后使用的引用模块的方法,所以我们称之为依赖前置

3. CMD

Cmd是SeaJs在推广过程中对模块定义的规范化产出
Cmd: 同步模块定义,是一个概念
SeaJs: SeaJS的作者是前淘宝UED,现支付宝前端工程师玉伯。
原则: 依赖就近原则

// 所有的模块通过define定义
   define(function (require, exports, module) {
       //通过require引入依赖,并不是AMD的前置依赖,而是依赖就近原则,在哪里使用,在哪里引入,就是同步的概念,即用即返回
           var $ = require('jquery');
           //输出模块中定义的方法
           exports.sayHello = function () {
               $('#hello').toggle('slow');
           };
       });

4. AMD和CMD的区别

AMD 通过 require.js实现
CMD 通过 sea.js实现

相同处 :
RequireJS 和 Sea.js 都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。

不同之处 :

  1. 定位有差异。RequireJS 想成为浏览器端的模块加载器,同时也想成为 Rhino / Node 等环境的模块加载器。Sea.js
    则专注于 Web 浏览器端,同时通过 Node 扩展的方式可以很方便跑在 Node 环境中。
  2. 遵循的规范不同。RequireJS 遵循 AMD(异步模块定义)规范,Sea.js 遵循 CMD
    (通用模块定义)规范。规范的不同,导致了两者 API 不同。Sea.js 更贴近 CommonJS Modules/1.1 和 Node
    Modules 规范。

  3. RequireJS 是依赖前置,Sea.js是依赖就近

  4. 对开发调试的支持有差异。Sea.js 非常关注代码的开发调试,有 nocache、debug 等用于调试的插件。RequireJS
    无这方面的明显支持。
  5. 插件机制不同。RequireJS 采取的是在源码中预留接口的形式,插件类型比较单一。Sea.js 采取的是通用事件机制,插件类型更丰富。

总之,如果说 RequireJS 是 Prototype 类库的话,则 Sea.js 致力于成为 jQuery 类库。

5. ES6

上面的AMD,CMD,CommonJs都是ES5时期的。
ES6中无需引入别的js文件,ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。
ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。

我这里只是简单说一下,推荐阮一峰老师的学习网站,里面有非常详细的讲解:

http://es6.ruanyifeng.com/#docs/module

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值