JavaScript 模块的发展史

我们都知道 JavaScript 中并没有模块的概念,一开始 JavaScript 的出现只是作为简单脚本语言来实现简单的页面逻辑,而随着互联网的发展和 web 2.0 时代的到来,前端代码呈现井喷式发展,随着代码量的增加,模块缺失的问题日益凸显,而同时 JavaScript 社区也做了很多探索。

那么什么是模块呢?

模块,是指能够单独命名并独立地完成一定功能的程序语句的集合(即程序代码和数据结构的集合体)。

所以,模块的核心就是需要完成特定的功能,并且其很重要的一点就是需要解决引用依赖以及被依赖的问题。

函数

从定义上来说,其实函数也可以被当作是一个模块。

在 myModule.js 中新增如下函数

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}

通过 script 标签引入 myModule.js 后可直接使用里面的函数

<script src="myModule.js"></script>
<script>
  console.log(add(1, 2)) // 3
  console.log(minus(2, 1)) // 1
</script>

在 myModule.js 中,每一个函数都可以被认为是一个模块。通过函数方式定义模块主要有两个缺陷:一是会污染全局变量,无法保证各模块间的变量名不冲突;二是需要手动维护依赖的顺序,如果 myModule.js 中的模块需要依赖其它模块(如 jQuery),则该模块(jQuery)需要在 myModule.js 之前引用。

CommonJS

自 2009 年 NodeJS 诞生后,JavaScript 模块化编程正式进入人们的视野,而 NodeJS 的模块化系统,便是参照 CommonJS 规范实现的。

在 CommonJS 规范中,一个文件就是一个模块,每个模块都有各自的作用域,即在一个模块中的变量、函数是私有的,外部无法访问。

在模块内部,module 变量对象代表当前模块,其 exports 属性也是一个对象,代表对外的接口,所以我们将需要对外暴露的内容放进 module.exprots 对象即可;而引用模块则使用 require 函数。

使用 CommonJS 规范来定义 myModule 模块,在 myModule.js 中写入如下代码

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}
module.exports = {
  add: add,
  minus: minus
}

在 main.js 中引用 myModule 模块

var myModule = require('./myModule.js');
console.log(myModule.add(1,2)); // 3
console.log(myModule.minus(2,1)); // 1

但是,由于在 CommonJS 规范中,require 加载模块的方式是同步加载,使得其不适合在浏览器端使用。在服务器端同步加载模块,等待时间取决于硬盘的读取时间,而在浏览器端同步加载模块,等待时间取决于网速快慢,这使得在等待加载模块的过程中,浏览器会处于假死的状态。

AMD(Asynchronous Module Definition)

AMD 即异步模块定义,是 RequireJS 在推广过程中对模块定义的规范化产出。同样的,其规定一个文件就是一个模块,文件名即模块名。

AMD 使用 define 函数定义模块;使用 require 函数引用模块。

define 函数使用方式如下

define(id?, dependencies?, factory);

定义 myModule.js 模块

define(['depenModule'], function (depenModule) {
  //do something
});

require 接收两个参数,第一个参数为所依赖的模块标识数组;第二个参数为依赖模块加载完成后的回调函数。

在 main.js 中引用 myModule 模块

require(['myModule'], function (myModule){
  //do something
});

AMD 推崇依赖前置,需要先异步加载其所需的依赖模块后才会执行相应回调函数中的代码。

CMD(Common Module Definition)

CMD 即公共模块定义,是 SeaJS 在推广过程中对模块定义的规范化产出。同样的,其规定一个文件就是一个模块,文件名即模块名。

其使用 define 函数定义模块;使用 require 函数引用模块。

define 函数使用方式如下

define(factory)

定义 myModule.js 模块

define(function(require, exports, module) {
  //do something
});

在 main.js 中引用 myModule 模块

var myModule = require('./myModule.js');
//do something

CMD 推崇依赖就近,在书写代码的过程中再根据其所需要的依赖 require 进来。

AMD 与 CMD 对于依赖的模块都是异步加载。 其最大的区别是对依赖模块的执行时机处理不同。

AMD 依赖前置,浏览器会立即加载其依赖模块;而 CMD 是依赖就近,需要将模块转为字符串解析才能确认其依赖模块并加载,这是一种牺牲性能来带来开发的便利性的做法。

UMD(Universal Module Definition)

UMD 即通用模块定义,是一种基本上可以在任何一个模块环境中工作的规范。

一段典型的 UMD 代码如下所示

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : // CMD
    typeof define === 'function' && define.amd ? define(['exports'], factory) : // AMD
      (factory((global['module'] = {}))); // Browser globals
}(this, (function (exports) {
  'use strict';

  exports.x = x;
  Object.defineProperty(exports, '__esModule', { value: true });
})));

其原理是通过对不同的环境的判断做相应的处理。

ES6 模块

在 ES6 中,JavaScript 终于有了自己真正模块的概念。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代之前的规范,成为浏览器和服务器通用的模块解决方案。

在 ES6 模块系统中,通过 export 和 export default 命令规定模块的对外接口,import 命令输入模块提供的功能。

myModule.js 中

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}

export {
  add,
  minus
}

main.js 中

import { add, minus } from './myModules.js';

console.log(add(1, 2)) // 3
console.log(minus(2, 1)) // 1

对于 ES6 的模块在此不多做介绍,参考文档见阮一峰大神的《 ECMAScript 6 入门 》

写在最后

在 JavaScript 模块化的探索道路上,出现了很多优秀的规范与框架,除了上述具有代表性的规范外,还有像 YUI、KMD 等其它优秀的规范框架。

虽然说 JavaScript 的模块化发展史(更确切的应该是 JavaScript 发展史)一路充满艰辛坎坷,但其实我们可以看到它正走在正确的道路上,越来越好,衷心希望未来 JavaScript 能够越来越强大,能够让我们在未来遇见更多的可能。

参考资料

JavaScript模块的前世今生

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

推荐阅读更多精彩内容