JavaScript模块化演进:从Module模式到ES6 Module详解

作者:闭包旅人

时间:2026-10-24

浏览:269

本文系统梳理JavaScript模块化发展历程,从解决全局污染问题的Module模式入手,深入解析CommonJS、AMD、CMD及ES6 Module四大核心机制。重点对比各规范在加载方式、依赖处理及输出特性上的差异,帮助开发者理解Node.js与浏览器端模块化实现的底层逻辑与适用场景。

文章目录

JavaScript模块化概述

模块化是JavaScript语言发展过程中的必然阶段,其核心价值在于协助开发者对代码进行拆分与结构化组织。

Module模式:封装与私有化

Module模式

在标准化的模块化规范确立之前,JavaScript开发者主要依赖Module设计模式来规避全局作用域污染。该模式最初源于传统软件工程,旨在为类提供私有与公有的封装机制。在JavaScript语境下,Module模式利用匿名函数自调用(即闭包)实现封装,并通过自定义暴露行为来明确区分私有成员与公有成员。


let myModule = (function (window) {
  let moduleName = 'module' // private
  // public
  function setModuleName(name) {
   moduleName = name
  }
  // public
  function getModuleName() {
   return moduleName
  }
  return { setModuleName, getModuleName } // 暴露行为
 })(window)

上述示例展示了Module模式的一种实现方式,它利用闭包特性创建独立作用域,从而缓解了全局命名冲突与安全性隐患。然而,该模式无法直接用于代码的组织与拆分,这促使了后续模块化规范的诞生。

CommonJS规范详解

模块化规范

1. CommonJS

CommonJS规范主要应用于Node.js开发环境,其核心概念是每个文件视为一个独立模块,拥有专属作用域。模块通过module.exports暴露公有成员。例如:


// 文件名:x.js
let x = 1;
function add() {
 x += 1;
 return x;
}
module.exports.x = x;
module.exports.add = add;

此外,CommonJS利用require()函数引入模块依赖,该函数支持引入Node内置模块、自定义模块以及npm第三方模块。


// 文件名:main.js
let xm = require('./x.js');
console.log(xm.x); // 1
console.log(xm.add()); // 2
console.log(xm.x);  // 1

从上述代码可见,require函数同步加载x.js,并返回module.exports输出字面量的拷贝值。有人可能疑惑:module.exports.x = x;看似赋值,为何如此运作?实际上,Module模式是模块化规范的基石,而CommonJS正是对Module模式的一种封装实现。我们可以完全使用Module模式复现上述代码效果:


let xModule = (function (){
 let x = 1;
 function add() {
  x += 1;
  return x;
 }
 return { x, add };
})();
let xm = xModule;
console.log(xm.x); // 1
console.log(xm.add()); // 2
console.log(xm.x);  // 1

通过Module模式模拟CommonJS原理,可以清晰解释其特性。由于CommonJS需通过赋值获取匿名函数自调用的返回值,因此require函数在加载模块时表现为同步。然而,这种加载机制限制了CommonJS在客户端的应用,因为通过HTTP同步加载模块耗时极高。

AMD与CMD规范对比

2. AMD和CMD

2.1 AMD


// 定义AMD规范的模块
define([function() {
 return 模块
})

与CommonJS不同,AMD规范中依赖的模块采用异步加载方式,定义的模块则作为回调函数执行,且依赖于require.js模块管理工具库。尽管AMD不采用匿名函数自调用进行封装,但仍可利用闭包原理实现模块私有与公有成员的区分:


define(['module1', 'module2'], function(m1, m2) {
 let x = 1;
 function add() {
  x += 1;
  return x;
 }
 return { add };
})

2.2 CMD

CMD是SeaJS在推广过程中对模块定义的规范化产出。AMD推崇依赖前置,而CMD推崇依赖就近。


define(function(require, exports, module) {
 // 同步加载模块
 var a = require('./a');
 a.doSomething();
 // 异步加载一个模块,在加载完成时,执行回调
 require.async(['./b'], function(b) {
  b.doSomething();
 });
 // 对外暴露成员
 exports.doSomething = function() {};
});
// 使用模块
seajs.use('path');

CMD集成了CommonJS与AMD的特点,同时支持同步和异步模块加载。CMD在加载依赖模块后并不立即执行,仅完成下载;待所有依赖模块加载完毕后进入主逻辑,遇到require语句时才执行对应模块。这使得模块执行顺序与书写顺序完全一致,且在CMD中同步加载模块时不存在HTTP请求过程。

ES6 Module特性分析

ES6 module

ES6的模块化已不再是外部规范,而是JavaScript语言内置特性。随着ES6的推出,AMD和CMD逐渐退出历史舞台。相比传统模块化规范,ES6模块具备两大显著特点:

  • 模块化规范输出的是值的拷贝,而ES6模块输出的是值的引用。
  • 模块化规范采用运行时加载,ES6模块则在编译时输出接口。

模块化规范输出的是一个对象,该对象仅在脚本运行结束后生成;而ES6模块并非对象,ES6 module采用多对象输出与多对象加载模型。从原理看,模块化规范是匿名函数自调用的封装,而ES6 module则是利用匿名函数自调用去调用输出的成员。两者区别可参考Babel and CommonJS modules。

以上为本次介绍的全部知识点,感谢大家的学习和对本站的支持。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章