单纯的文件划分

最早开始的模块化基于简单的相互约定,用文件的形式进行划分,每一个 <script> 标签代表一个模块引入进 HTML 文件中。

  1. <!-- 假定模块a下有函数foo() -->
  2. <script src="module-a.js"></script>
  3. <!-- 假定模块b下有变量bar -->
  4. <script src="module-b.js"></script>
  5. <script>
  6. foo() // 在引入多个文件的情况下,foo()可能存在命名的冲突
  7. bar = 'foo' // 数据变量可能会被更改且无法知道是由谁更改的
  8. </script>

此类模块化在面对大型开发时暴露的缺点包括:

  • 无法确定模块间彼此的依赖关系。
  • 无私有空间,所有方法与变量都会污染全局作用域,容易出现命名冲突/被意外修改的情况。
  • 无法明确模块内容(无法简单判断出 foo() 是来自于a模块还是b模块)。

针对以上缺点,产生了一些派生的改进。

命名空间形式

针对命名冲突/无法明确方法来源的问题,引入了命名空间的形式:每个模块只对外部暴露一个Object对象,所有需要的数据与方法都写在这个对象内。

  1. <!-- 模块foo -->
  2. <script>
  3. window.foo = {
  4. data:'foo',
  5. method: function() {alert(`${this.data}'s method`)}
  6. }
  7. </script>
  8. <!-- 模块bar -->
  9. <script>
  10. window.bar = {
  11. data:'bar',
  12. method: function() {alert(`${this.data}'s method`)}
  13. }
  14. </script>
  15. <script>
  16. // 通过引入模块名的命名空间解决了命名冲突的问题
  17. foo.method() // alert "foo's method"
  18. bar.method() // alert "bar's method"
  19. // 内部数据仍然不受控的会被修改和重写
  20. foo.data = 'bar'
  21. </script>

如上所示,只解决了命名会冲突的问题的模块化虽然有所进步,但依旧是不够的:外部代码依旧可以对本该私有的模块内部数据进行修改。

IIFE与有带依赖的IIFE

命名空间形式所存在的问题在于其无法拥有一个私有的作用域,而无法拥有私有成员对于模块的自组织无疑是致命的。

IIFE的定义、特性与用法

定义

立即执行函数表达式(IIFE,Immediately-Invoked Function Expression)是一个拥有自己的词法作用域的自执行函数,其执行的时间就是这个函数被定义的时间。

A Javascript function that runs as soon as it is defined. -MDN

特性与用法

  1. var result = (function () {
  2. var name = 'foo';
  3. return name;
  4. })();
  5. result; // "foo"
  6. name; // "Uncaught ReferenceError: name is not defined"

由以上的代码可以总结出 IIFE 在模块化场景下的最大优势:函数拥有自己独立的词法作用域,使用这个特性可以实现私有成员,且不会对全局作用域污染。

IIFE形式

  1. ;(function () {
  2. var name = 'foo'
  3. function method () {
  4. console.log(`${name}'s method`)
  5. }
  6. window.moduleA = {
  7. method
  8. }
  9. })()

可以看出,这种形式只是在前面命名空间形式上包裹了一层 IIFE 函数,但通过这种包裹,我们已经实现了模块化中的私有成员需求,对比最初的文件划分形式,到此已经解决了命名冲突与私有空间的问题,只剩下一个缺点:
如何解决模块之间相互依赖的问题。

带依赖的IIFE形式

jQuery 时代的插件注册是这种形式最常见的地方,通过指定传入 IIFE 的参数,较为优雅的体现了模块间彼此依赖的关系,同时也符合代码设计中的单一职责原则。在出现具体的模块化标准之前,该形式已经是刀耕火种时代的终极形式。

  1. (function($){
  2. $.method();
  3. })(jQuery);

在痛点解决之后,下面要解决的的就是更多满足开发者的爽点,提升效率的阶段,为了实现解决各类开发维护的痛点,模块标准化的出现了。