JS降生之初面向简朴页面开辟, 没有模块的观点。厥后页面逐步庞杂, 人类组织到 IIFE 马上实行函数来模仿 模块;之前也有雅虎的实践,运用定名空间 作为模块名。末了衍生出 面向种种运用场景 的 JS 模块范例。比方:
面向浏览器的 AMD
面向Nodejs的 CommonJS
关于这类破裂状况ES范例也在全力弥合。 然则现在盛行的实践是 UMD情势。
1 AMD
AMD 是requirejs 推行产出的范例,重要用于浏览器环境,经由过程define和require这两个定义模块、挪用模块。
定义模块
define("alpha", ["require", "exports", "beta"], function (require, exports, beta) {
exports.verb = function() {
return beta.verb();
//Or:
return require("beta").verb();
}
});
// 返回对象的匿名模块
define(["alpha"], function (alpha) {
return {
verb: function(){
return alpha.verb() + 2;
}
};
});
挪用模块
require(['foo', 'bar'], function ( foo, bar ) {
foo.doSomething();
});
define(function (require) {
require(['a', 'b'], function (a, b) {
//modules a and b are now available for use.
});
});
2 commonJS
Node 运用由模块组成,采纳 CommonJS 模块范例。
每一个文件就是一个模块,有本身的作用域。在一个文件内里定义的变量、函数、类,都是私有的,对其他文件不可见。
CommonJS 加载模块是同步的,所以只要加载完成才实行背面的操纵。像Node.js重要用于服务器的编程,加载的模块文件平常都已存在当地硬盘,所以加载起来比较快,不必斟酌异步加载的体式格局,所以CommonJS范例比较实用。但如果是浏览器环境,要从服务器加载模块,这是就必须采纳异步情势。所以就有了 AMD CMD 解决方案。
// a.js
// 相称于这里另有一行:var exports = module.exports;代码
exports.a = 'Hello world'; // 相称于:module.exports.a = 'Hello world';
// b.js
var moduleA = require('./a.js');
console.log(moduleA.a); // 打印出hello world
3 UMD
兼容 AMD 和 commonjs,也兼容 全局变量定义的 通用的模块化范例
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS-like
module.exports = factory(require('jquery'));
} else {
// Browser globals (root is window)
root.returnExports = factory(root.jQuery);
}
}(this, function ($) {
function a(){}; // 私有要领,由于它没被返回 (见下面)
function b(){}; // 大众要领,由于被返回了
function c(){}; // 大众要领,由于被返回了
// 暴露大众要领
return {
b: b,
c: c
}
}));
4 ES6 Module
ES6 在言语范例的层面上,完成了模块功用,而且完成得相称简朴,旨在成为浏览器和服务器通用的模块解决方案。其模块功用重要由两个敕令组成:export和import。
export敕令用于划定模块的对外接口,导出模块暴露的api ;import敕令用于输入其他模块供应的功用,引入其他模块。
/** 定义模块 math.js **/
var basicNum = 0;
var add = function (a, b) {
return a + b;
};
export { basicNum, add };
/** 援用模块 **/
import { basicNum, add } from './math';
function test(ele) {
ele.textContent = add(99 + basicNum);
}
5 ES6 模块与 CommonJS 模块的差别
- CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的援用。
- CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
- ES6 模块的运转机制与 CommonJS 不一样。JS 引擎对剧本静态剖析的时刻,碰到模块加载敕令import,就会天生一个只读援用。比及剧本真正实行时,再依据这个只读援用,到被加载的谁人模块内里去取值。换句话说,ES6 的import有点像 Unix 体系的“标记衔接”,原始值变了,import加载的值也会随着变。因而,ES6 模块是动态援用,而且不会缓存值,模块内里的变量绑定其地点的模块。
- CommonJS 模块是运转时加载,ES6 模块是编译时输出接口。
- 运转时加载: CommonJS 模块就是对象;即在输入时是先加载全部模块,天生一个对象,然后再从这个对象上面读取要领,这类加载称为“运转时加载”。
- 编译时加载: ES6 模块不是对象,而是经由过程 export 敕令显式指定输出的代码,import时采纳静态敕令的情势。即在import时能够指定加载某个输出值,而不是加载全部模块,这类加载称为“编译时加载”。
CommonJS 加载的是一个对象(即module.exports属性),该对象只要在剧本运转完才会天生。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态剖析阶段就会天生。