AMD CMD CommonJS ES6 Module

2020-11-17  本文已影响0人  张Piers

前言

作为一个接口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的区别

上一篇下一篇

猜你喜欢

热点阅读