JS模块化

JavaScript 模块化体系在长期的实践中逐步演化,主要包括 AMD、CMD、CommonJS、RequireJS、Sea.js 等模块规范或工具,它们从不同场景出发,解决了早期 JavaScript 缺乏模块机制的问题。

CommonJS 是 Node.js 采用的模块化规范,每个文件就是一个模块,通过 require() 引入其他模块,使用 module.exports 导出模块内容。它的特点是同步加载模块,适用于服务器端环境,因为模块文件都存在本地磁盘,加载速度快。其底层本质是将模块包裹在一个函数作用域中运行,通过缓存机制避免重复加载。

AMD(Asynchronous Module Definition) 是为了解决浏览器端模块加载的异步需求而提出的规范。代表实现是 RequireJS。AMD 使用 define(id?, deps?, factory) 定义模块,支持提前声明依赖、异步并发加载模块。这种设计非常适合浏览器环境下的资源按需加载,依赖在模块加载前就被解析出来,利于并发加载优化。但写法相对复杂,阅读性较弱。

CMD(Common Module Definition) 则是 Sea.js 所采用的模块化规范。与 AMD 不同,CMD 强调依赖就近使用,采用 define(function(require, exports, module){ ... }) 的写法,模块中的 require 可以在需要的地方调用,依赖是在运行时动态解析的。CMD 更贴近 CommonJS 的风格,适合以按需加载为导向的前端项目。但它的运行时依赖分析机制,可能会影响并发加载效率。

RequireJS 是对 AMD 规范的一个实现,它为浏览器环境提供了模块定义与异步加载能力,支持模块依赖管理、打包优化和懒加载等。开发者需通过 require.config() 配置模块路径、别名等信息。

Sea.js 是国人提出的前端模块加载器,实现了 CMD 规范,主张“依赖就近”,适用于脚本按需加载的情境。Sea.js 强调轻量、灵活、简单,适合中小型前端项目,其模块通过 define 定义并用 seajs.use() 使用,配合 aliaspaths 机制来管理模块路径。

总结来说,CommonJS 面向服务器,采用同步加载;AMD 适合浏览器,采用异步并发加载且依赖前置;CMD 同样面向浏览器,但依赖就近、按需执行;RequireJS 是 AMD 的实现;Sea.js 是 CMD 的实现。 这几种规范各自出现在不同的时间与技术背景下,最终都为 ES6 的模块系统(ESM)铺平了道路。


暂时性死区

异步与作用域

1
2
3
4
5
6
7
8
9
10
for (let i = 0; i < 2; i++) {
setTimeout(function () {
console.log(i)
}, 100);
}
for (var i = 0; i < 2; i++) {
setTimeout(function () {
console.log(i)
}, 100);
}

第一个for循环使用let声明变量i,由于let具有块级作用域,每次循环都会创建一个新的作用域,每个setTimeout里的i都是当时循环的值。因此会打印0和1。

第二个for循环使用var声明变量i,var没有块级作用域,它是函数作用域。当循环结束时i的值为2,而setTimeout是异步执行的,等到真正执行时,两个回调函数中的i都指向同一个变量,此时i的值已经是2了。因此会打印两个2。

所以最终的打印顺序是:0 1 2 2