模块化演进全景:全局 → 命名空间 → IIFE → 规范

本节目标

用一条时间线看「为什么变成今天这样」:

① 全局变量        → 污染、覆盖(项目一大就乱)
② 命名空间对象    → let App = {}; App.user=...(缓解污染,但仍是全局、要手动约定)
③ IIFE            → (function(){...})()(真正隔离作用域,私有变量成为可能)
④ CJS/AMD/UMD     → 社区规范:显式依赖、可加载(服务端 CJS / 浏览器 AMD)
⑤ ESM             → 语言标准:静态可分析、Tree Shaking、浏览器原生(一统江湖)

每一步都是「前一个方案的痛点」逼出来的:命名空间缓解污染但不隔离;IIFE 隔离但没「依赖声明」;规范补了依赖声明但语法杂、要加载器;ESM 把一切收编进语言标准。

名词解释

命名空间(Namespace):把一类变量挂到一个全局对象下(如 const App = { user:{}, util:{} }),减少顶层全局数量。它是「污染缓解」的过渡方案,但对象本身还是全局、且靠人工约定不冲突,隔离不彻底。

演进驱动力:技术形态由「当时的痛点」推动。没有模块化时全局乱;有了 IIFE 能隔离但依赖关系隐式;社区规范显式化依赖却各自为政;ESM 最终用语言标准统一。看历史就是看「每一步在补哪个洞」。

课后练习

练习 1:命名空间和 IIFE,谁真正「隔离了作用域」?

答案:IIFE。命名空间只是「把变量收进一个全局对象」,对象本身还是全局、内部变量仍可被外部改;IIFE 用函数作用域让内部变量「外部完全碰不到」,才是真隔离。命名空间是「约定级」缓解,IIFE 是「语法级」隔离。

练习 2:为什么 ESM 能「一统」CJS/AMD,而不是共存?

答案:ESM 是语言标准、浏览器原生支持、且静态可分析(Tree Shaking),在「写法统一 + 工程优化 + 免加载器」上全面优于社区规范。CJS 仍因 Node 生态保留,但新代码全面 ESM,历史规范自然退场(老项目里还能见到)。

总结

把演进线串起来,我的体会是:模块化不是某个天才的设计,而是被规模化和协作需求一步步逼出来的。每一代都在补上一代的洞——全局太乱→命名空间收拢→还不够→IIFE 隔离→还缺依赖声明→规范补上→还乱(规范太多)→ESM 统一。这个视角让你面对任何「新技术」都能问对问题:「它解决了前一个的什么痛点?」而不是盲目追新。对老代码也多一份同理:它用 AMD/UMD 不是落后,是「当年的最优解」。理解演进,你就有了技术判断力。