require 命令是 CommonJS 规范之中,用来加载其他模块的命令。它其实不是一个全局命令,而是指向当前模块的 module.require 命令,而后者又调用 Node 的内部命令 Module._load。
Module._load = function(request, parent, isMain) { // 1. 检查 Module._cache,是否缓存之中有指定模块 // 2. 如果缓存之中没有,就创建一个新的Module实例 // 3. 将它保存到缓存 // 4. 使用 module.load() 加载指定的模块文件, // 读取文件内容之后,使用 module.compile() 执行文件代码 // 5. 如果加载/解析过程报错,就从缓存删除该模块 // 6. 返回该模块的 module.exports }; Module.prototype._compile = function(content, filename) { // 1. 生成一个require函数,指向module.require // 2. 加载其他辅助方法到require // 3. 将文件内容放到一个函数之中,该函数可调用 require // 4. 执行该函数 };所以,其实 CommonJS 的模块化规范之所以可以实现控制作用域、模块依赖、模块通信,其实本质上就是将模块内的代码都放在一个函数内来执行,这过程中会创建一个对象 Module,然后将模块的相关信息包括对外的接口信息都添加到对象 Module 中,供其他模块使用。
AMD规范&Require.js(2009年)
CommonJS 规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。由于 Node.js 主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS 规范比较适用。
但是,如果是浏览器环境,这种同步加载文件的模式就会导致浏览器陷入卡死状态,因为网络原因是不可控的。所以,针对浏览器环境的模块化,新提出了一种规范:AMD(Asynchronous Modules Definition)异步模块定义。
也就是说,对于 Node.js,对于服务端而言,模块化规范就是按照 CommonJS 规范即可。
但对于浏览器,对于前端而言,CommonJS 不适用,需要看看 AMD 规范。
AMD 规范中定义:
定义一个模块时通过 define 命令,通过 return 声明模块对外暴露的接口
依赖其他模块时,通过 require 命令
而规范终归只是规范,使用时还是需要有规范的具体实现,针对 AMD 规范,具体的实现是 Require.js,在前端里,如果基于 Require.js 来使用 AMD 规范的模块化技术,后续介绍。
CMD规范&Sea.js(2013年)
CMD(Common Module Definition)也是专门针对浏览器、针对前端而提出的一种模块化规范。它跟 AMD 规范都是用途都是一样,用途解决前端里的模块化技术。
但两种规范各有各的优缺点,各有各的适用场景和局限性吧,我还没使用过这两种规范,所以无从比较,但网上关于这两种规范比较的文章倒是不少。
CMD 规范中定义:
使用 define 命令定义一个模块,使用 exports 来暴露模块对外的接口
使用 require 来同步加载依赖的模块,但也可使用 require.async 来异步加载依赖的模块
总之,虽然两种规范都是用于解决前端里的模块化技术,但实现的本质上还是有些不同,后续介绍。
对于 CMD 规范的具体实现是 Sea.js,前端里如果想使用 CMD 规范的模块化技术,需要借助 Sea.js。
ES6标准(2015年)
2015 年发布的 ES6 标准规范中,新增了 Module 特性,也就是官方在 ES6 中,在语言本身层面上,添加了模块的机制支持,让 JavaScript 语言本身终于可以支持模块特性了。
在 ES6 之前的各种方案,包括 CommonJS,AMD,CMD,本质上其实都是利用函数具有的本地变量的特性进行封装从而模拟出模块机制。也就是说,这些方案都是需要在运行期间,才会动态的创建出某个模块,并暴露模块的相关接口。这种方案其实也存在一些局限性。
而 ES6 新增了模块的机制后,在代码的解析阶段,就能够确定模块以及模块对外的接口,而不用等到运行期。这种本质上的区别,在借助开发工具写代码阶段的影响很明显就是,终于可以让开发工具智能的提示依赖的模块都对外提供了哪些接口。
但 ES6 由于是新增的特性,在支持方面目前好像还不是很理想,并不是所有环境都支持 ES6 的模块机制好像,所以会看到某些大佬的文章里会介绍一些诸如:Babel、Browserify。
Babel 用于将 ES6 转换为 ES5 代码,好让不支持 ES6 特性的环境可以运行 ES5 代码。
Browserify 用于将编译打包 js,处理 require 这些浏览器并不认识的命令。