模块与导入

JavaScript 有个特点:大量常用功能是全局可用的MathArrayMapSetconsoleJSON 这些直接就能用,不需要导入。和 Python、Java 比起来,你需要手动 import 的东西其实不多。

不过在 Node.js 环境中(LeetCode 和本站的 JS 运行环境都是 Node.js),有些模块还是需要用 require 来加载的。这篇文章来讲讲 JavaScript 的模块系统。

Node.js 模块系统(CommonJS)

Node.js 使用 CommonJS 模块规范,核心就两个东西:

  • require() 导入模块
  • module.exports 导出内容
// 导入内置模块
const os = require('os');

// 导入自定义文件
const utils = require('./utils');

// 导入第三方包
const { PriorityQueue } = require('@datastructures-js/priority-queue');

导入自定义文件时,路径要以 ./../ 开头,表示相对路径。不加前缀的话,Node.js 会去 node_modules 目录找第三方包。

刷题常用模块和内置对象

JavaScript 很多常用功能是全局可用的,不需要 import:

全局对象用途
Math数学函数(maxminfloorceilabssqrt
JSONJSON 解析(parsestringify
ArrayMapSet内置数据结构
parseIntparseFloat类型转换
console.log输出

需要 require 导入的:

模块用途导入方式
readlineACM 模式读取标准输入require('readline')
@datastructures-js/priority-queue优先队列(LeetCode/本站预装)require('@datastructures-js/priority-queue')

具体的用法和示例会在后续的 常用数据结构算法题常用技巧 章节中详细介绍。

ES6 模块(了解即可)

除了 CommonJS,JavaScript 还有一套更现代的模块系统叫 ES6 模块(ESM),用 import/export 语法:

// 导出
export function add(a, b) { return a + b; }
export default class Calculator { ... }

// 导入
import { add } from './math.js';
import Calculator from './calculator.js';

ES6 模块和 CommonJS 的主要区别:ES6 是静态导入,在编译时就确定了依赖关系;CommonJS 是动态加载,运行时才去找模块。

目前大多数 OJ 平台和 LeetCode 的 JavaScript 环境仍然用 CommonJS(require),所以你刷题时用 require 就行。了解 ES6 模块主要是为了以后写前端项目时用。

导出模块内容

在 CommonJS 中,一个文件要对外提供函数或变量,需要通过 module.exports 导出:

// math_utils.js
function factorial(n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
}

function isPrime(n) {
    if (n < 2) return false;
    for (let i = 2; i * i <= n; i++) {
        if (n % i === 0) return false;
    }
    return true;
}

// 把要导出的函数挂到 module.exports 上
module.exports = { factorial, isPrime };

其他文件就可以 require 进来使用了:

// main.js
const { factorial, isPrime } = require('./math_utils');
console.log(factorial(5));  // 120
console.log(isPrime(7));    // true

下面是一个完整的多文件项目示例:

了解了模块和导入之后,下一篇我们来讲 函数与闭包。函数是 JavaScript 中组织代码的核心手段,箭头函数和闭包也很有特色,学完之后你就能写出更有结构的代码了。