1.模块化 #

模块化是在 JavaScript 中管理和组织代码的重要方式,它可以帮助我们将代码拆分成多个可重用和可测试的单元。这不仅可以提高代码的可读性和可维护性,也可以避免命名冲突和全局作用域的污染。 在 JavaScript 中有几种模块化的方法,包括 CommonJS,AMD,UMD 和 ES6 Modules

2. CommonJS #

CommonJS 是服务器和桌面环境(主要是 Node.js)中使用的一种模块系统规范。它的核心思想是,通过 require 方法来同步加载依赖的其他模块,通过 exportsmodule.exports 来导出模块的公共接口。

下面是 CommonJS 的基本用法:

// math.js
exports.add = function(a, b) {
  return a + b;
};

exports.subtract = function(a, b) {
  return a - b;
};

在上面的代码中,math.js 文件导出了 addsubtract 两个函数。然后,我们可以在另一个文件中使用 require 来导入这些函数:

// app.js
var math = require('./math.js');

console.log(math.add(1, 2)); // 输出:3
console.log(math.subtract(1, 2)); // 输出:-1

我们也可以使用 module.exports 来导出单一值或者一个函数:

// math.js
module.exports = function add(a, b) {
  return a + b;
};

// app.js
var add = require('./math.js');

console.log(add(1, 2)); // 输出:3

注意,CommonJS 是同步加载模块的。当 require 调用发生时,Node.js 会阻塞,直到脚本已经被下载、解析并执行,然后返回输出对象。这在服务器端是没有问题的,因为模块都已经被下载到本地硬盘,所以加载起来非常快。但在浏览器端,加载模块可能需要下载,这就会造成阻塞,用户可能要等待很长时间,这是 CommonJS 规范不适用于浏览器环境的主要原因。

3. AMD #

AMD (Asynchronous Module Definition) 是一种 JavaScript 模块化规范,主要用于浏览器环境。其主要目标是提供一种能够异步加载模块和库的机制。AMD 规范主要由 RequireJS 提出并实施。

在 AMD 规范中,一个模块被定义为一个 JavaScript 文件。每个文件中都包含了一个 define 函数,该函数用于定义模块的依赖和输出。

下面是一个 AMD 模块的示例:

// math.js
define(function () {
  return {
    add: function (a, b) {
      return a + b;
    },
    subtract: function (a, b) {
      return a - b;
    },
  };
});

在这个例子中,math.js 文件通过 define 函数定义了一个模块。该模块没有依赖任何其他模块,并且它导出了一个对象,该对象有两个方法:addsubtract

然后,我们可以在其他文件中使用 require 函数来异步加载和使用这个模块:

html

    <script src="https://unpkg.com/requirejs@2.3.6/require.js"></script>
    <script>
        require(['math'], function (math) {
            console.log(math.add(1, 2)); // 输出:3
            console.log(math.subtract(1, 2)); // 输出:-1
        });
    </script>

在这个例子中,require 函数接受两个参数:一个依赖数组和一个回调函数。当所有的依赖都已经加载并执行完毕时,回调函数就会被调用。回调函数的参数是依赖模块的输出。

AMD 的主要优点是支持异步加载,使得模块可以在需要时再加载,而不是在页面加载时就加载。这对于大型、复杂的 Web 应用来说非常有用。然而,AMD 的语法比较复杂,使用起来可能比其他模块化方案更困难。

4. ES6 Modules #

ES6 Modules 是 ES2015(也称为 ES6)引入的 JavaScript 中的官方模块系统。ES6 Modules 旨在成为 JavaScript 的标准模块系统,使得 JavaScript 在语言级别上支持模块化。

在 ES6 Modules 中,一个模块是一个文件,文件中的每个变量、函数或类都是私有的,除非它们被显式导出。同样,模块不能访问其他模块中的变量、函数或类,除非它们被显式导入。

导出

你可以使用 export 关键字来导出你想从模块中共享的任何顶级函数、类、变量或对象。例如:

// math.js
export function add(a, b) {
  return a + b;
}

export function subtract(a, b) {
  return a - b;
}

此外,你还可以使用 export default 导出单一的默认函数或值。一个模块只能有一个默认导出:

// math.js
export default function add(a, b) {
  return a + b;
}

导入

你可以使用 import 关键字从其他模块导入你需要的函数、对象或值。例如:

// app.js
import { add, subtract } from './math.js';

console.log(add(1, 2)); // 输出:3
console.log(subtract(1, 2)); // 输出:-1

如果你要导入默认导出,你可以这样做:

// app.js
import add from './math.js';

console.log(add(1, 2)); // 输出:3

如果你想导入一个模块的所有导出,你可以使用 *

// app.js
import * as math from './math.js';

console.log(math.add(1, 2)); // 输出:3
console.log(math.subtract(1, 2)); // 输出:-1

注意

由于 ES6 Modules 是静态的,这意味着你不能动态导入或导出。所有的导入和导出必须位于模块的顶层作用域中,不能位于函数内部、if 语句内或任何其他块内。

在浏览器中,为了使用 ES6 Modules,你需要在你的 script 标签中添加 type="module" 属性:

<script type="module" src="app.js"></script>

5. UMD #

UMD(Universal Module Definition)是一个JavaScript模块定义模式,它试图提供一种方式,可以在多种JavaScript加载系统下运行。UMD的目标是使一个模块可以在客户端和服务器上同时工作。

UMD实现的基本思路是首先判断是否支持Node.js的模块(exports)特性,如果支持,则使用Node.js模块模式;接着判断是否支持AMD(define)特性,如果支持,那么就使用AMD方式加载模块;如果前两个都不支持,则采用全局变量的方式。

下面是一个UMD模块的典型实现模式:

(function (root, factory) {
  if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define(['b'], factory);
  } else if (typeof module === 'object' && module.exports) {
    // Node. Does not work with strict CommonJS, but
    // only CommonJS-like environments that support module.exports,
    // like Node.
    module.exports = factory(require('b'));
  } else {
    // Browser globals (root is window)
    root.returnExports = factory(root.b);
  }
}(typeof self !== 'undefined' ? self : this, function (b) {
  // Use b in some fashion.

  // Just return a value to define the module export.
  // This example returns an object, but the module
  // can return a function as the exported value.
  return {};
}));

在上述代码中,factory函数就是模块的定义。依赖的模块在这个例子中是'b',在AMD和CommonJS环境下,它通过参数传递到factory函数。在全局变量的环境下,它通过root.b来获取。

在浏览器全局变量环境下,模块的输出被附加到root对象(通常是window)。

UMD的主要用途是使你的模块在各种环境中都能被正确的加载,包括AMD、CommonJS以及浏览器全局变量环境。