AMD CMD CommonJS ES6 Module

前言

作为一个接口API测试工程师,你是否曾经有过疑惑,有些项目在模仿(fu zhi zhan tie)原有的代码时,有时候是通过export 将模块暴露出去,有时候是通过export default,有些包可以通过import引用,有些是require,那么,来总结一下这些区别

首先了解一些基本的概念

AMD(RequireJS)

  1. AMD一开始是CommonJS规范中的一个草案,全称是Asynchronous Module Definition,即异步模块加载机制。后来由该草案的作者以RequireJS实现了AMD规范,所以一般说AMD也是指RequireJS。
  2. 特点:
    • 对于依赖的模块,AMD推崇依赖前置,提前执行。也就是说,在define方法里传入的依赖模块(数组),会在一开始就下载并执行。
    • 模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
  3. 使用:通过define来定义一个模块,使用require可以导入定义的模块。
// a.js
/*
* define(id?, dependencies?, factory);
* id:可选参数,用来定义模块的标识,如果没有提供该参数,脚本文件名(去掉拓展名)
* dependencies:是一个当前模块依赖的模块名称数组
* factory:工厂方法,模块初始化要执行的函数或对象。如果为函数,它应该只被执行一次。如果是对象,此对象应该为模块的输出值 在页面上使用require函数加载模块
*/
define(function(){
    return 1;
})

// b.js
/*
*数组中声明需要加载的模块,可以是模块名、js文件路径
* require([dependencies], callback); 
* [dependencies]: 是一个数组,表示所依赖的模块
* callback: 是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块
*/
require(['a'], function(a){
   console.log(a);// 1
});

CMD

  1. CMD是SeaJS在推广过程中生产的对模块定义的规范,在Web浏览器端的模块加载器中,SeaJS与RequireJS并称,SeaJS作者为阿里的玉伯。
  2. 特点:对于依赖的模块,CMD推崇依赖就近,延迟执行。也就是说,只有到require时依赖模块才执行。
  3. 使用:
// a.js
/*
* define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串
* define(id?, deps?, factory)
*  id 表示模块标识
* 数组 deps 是模块依赖
* factory 为对象、字符串时,表示模块的接口就是该对象、字符串。
*/
define(function(require, exports, module) {
  var $ = require('jquery');
  exports.setColor = function() {
    $('body').css('color','#333');
  };
});

//b.js
//数组中声明需要加载的模块,可以是模块名、js文件路径
seajs.use(['a'], function(a) {
  $('#el').click(a.setColor);
});

CommonJS

  1. CommonJS规范为CommonJS小组所提出,目的是弥补JavaScript在服务器端缺少模块化机制,NodeJS、webpack都是基于该规范来实现的。
    在 ES6 之前,ECMAScript 并没有提供代码组织的方式,那时候通常是基于 IIFE 来实现“模块化”,随着 JavaScript 在前端大规模的应用,以及服务端 Javascript 的推动,原先浏览器端的模块规范不利于大规模应用。于是早期便有了 CommonJS 规范,其目标是为了定义模块,提供通用的模块组织方式。
  2. 特点:
    • 所有代码都运行在模块作用域,不会污染全局作用域;
    • 模块是同步加载的,即只有加载完成,才能执行后面的操作;
    • 模块在首次执行后就会缓存,再次加载只返回缓存结果,如果想要再次执行,可清除缓存;
    • require返回的值是被输出的值的拷贝,模块内部的变化也不会影响这个值。
  3. 使用:
//a.js
module.exports = function () {
  console.log("hello world")
}

//b.js
var a = require('./a');

a();//"hello world"

//或者

//a2.js
exports.num = 1;
exports.obj = {xx: 2};

//b2.js
var a2 = require('./a2');

console.log(a2);//{ num: 1, obj: { xx: 2 } }

ES6 Module

  1. ES6 Module是ES6中规定的模块体系,相比上面提到的规范, ES6 Module有更多的优势,有望成为浏览器和服务器通用的模块解决方案。
  2. 特点(对比CommonJS):
    • CommonJS模块是运行时加载,ES6 Module是编译时输出接口;
    • CommonJS加载的是整个模块,将所有的接口全部加载进来,ES6 Module可以单独加载其中的某个接口;
    • CommonJS输出是值的拷贝,ES6 Module输出的是值的引用,被输出模块的内部的改变会影响引用的改变;
    • CommonJS this指向当前模块,ES6 Module this指向undefined;
    • 目前浏览器对ES6 Module兼容还不太好,我们平时在webpack中使用的export/import,会经过babel转换为CommonJS规范。
  3. 使用:
//a.js
var name = 'lin';
var age = 13;
var job = 'ninja';

export { name, age, job};

//b.js
import { name, age, job} from './a.js';
console.log(name, age, job);// lin 13 ninja

//或者

//a2.js
export default function () {
  console.log('default ');
}

//b2.js
import customName from './a2.js';
customName(); // 'default'

Node.js模块里exports与module.exports的区别

其实require导出的内容是module.exports的指向的内存块内容,并不是exports的。 简而言之,区分他们之间的区别就是 exports 只是 module.exports的引用,辅助后者添加内容用的。
为了避免糊涂,尽量都用 module.exports 导出,然后用require导入。

var module = {
    exports:{
        name:"我是module的exports属性"
    }
};
var exports = module.exports;  //exports是对module.exports的引用,也就是exports现在指向的内存地址和module.exports指向的内存地址是一样的
console.log(module.exports);    //  { name: '我是module的exports属性' }
console.log(exports);   //  { name: '我是module的exports属性' }
exports.name = "我想改一下名字";console.log(module.exports);    //  { name: '我想改一下名字' }
console.log(exports);   //  { name: '我想改一下名字' }
//看到没,引用的结果就是a和b都操作同一内存地址下的数据


//这个时候我在某个文件定义了一个想导出的模块
var Circle = {
    name:"我是一个圆",
    func:function(x){
        return x*x*3.14;
    }
};
exports = Circle;  //   看清楚了,Circle这个Object在内存中指向了新的地址,所以exports也指向了这个新的地址,和原来的地址没有半毛钱关系了
console.log(module.exports);    //  { name: '我想改一下名字' }
console.log(exports);   // { name: '我是一个圆', func: [Function] }

ES中的模块重的export 和 export default的区别

  • export与export default均可用于导出常量、函数、文件、模块等
  • 在一个文件或模块中,export、import可以有多个,export default仅有一个
  • 通过export方式导出,在导入时要加{ },export default则不需要
  • export能直接导出变量表达式,export default不行。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容