2.2 Node的模块实现

好,我们深入第二章的第二小节:2.2 Node的模块实现

这一小节是第二章的核心部分,朴灵作者详细剖析了Node.js如何从源码层面实现CommonJS的require机制。整个过程分为三个关键步骤:优先从缓存加载路径分析和文件定位模块编译。理解这里,你就知道为什么require这么高效、为什么有缓存、为什么循环依赖不会死锁等"黑魔法"。

下面是这一小节的子结构(原书划分):

  • 2.2.1 优先从缓存加载
  • 2.2.2 路径分析和文件定位
  • 2.2.3 模块编译

详细讲解

2.2.1 优先从缓存加载

这是require最聪明的设计:模块只执行一次,后续直接返回缓存的exports对象。

  • 原理:Node用Module._cache(一个对象,以模块绝对路径为key)缓存已加载模块。
  • 第一次require('foo'):加载、执行、缓存module.exports。
  • 第二次require('foo'):直接从缓存取,返回同一个对象引用。

示例代码(书中有类似演示):

js 复制代码
// a.js
console.log('a.js 执行了');
exports.done = false;

// b.js
var a = require('./a');
console.log('b.js 中,a.done =', a.done);
a.done = true;
console.log('b.js 中,修改后 a.done =', a.done);

// main.js
var a = require('./a');
var b = require('./b');
console.log('main.js 中,a.done =', a.done);

输出:

ini 复制代码
a.js 执行了  // 只打印一次!
b.js 中,a.done = false
b.js 中,修改后 a.done = true
main.js 中,a.done = true

关键点:

  • 模块代码只执行一次,避免副作用重复。
  • 导出的是对象引用,修改会共享(这是循环依赖能工作的基础:先导出空对象{})。

2.2.2 路径分析和文件定位

require的参数(模块标识符)可能是核心模块、相对路径、绝对路径或包名。Node按优先级分析:

  1. 核心模块(如'fs'、'http'):直接从内置加载,最快。
  2. 文件模块
    • 绝对路径:直接定位。
    • 相对路径(./ 或 ../):从调用者目录解析。
    • 无扩展名:依次尝试 .js → .json → .node(C++扩展)。
  3. 包模块 (如require('express')):
    • 在当前目录的node_modules找。
    • 找不到?向上级目录逐级查找,直到根目录。
    • 找到目录后,读package.json的"main"字段(默认index.js)。

这个过程在源码的Module._resolveFilename函数实现。书里强调:这是NPM能工作的基础!

2.2.3 模块编译

找到文件后,Node不直接执行,而是包裹成一个函数运行,创造独立作用域。

  • 对于.js文件:读取内容,前面加(function (exports, require, module, __filename, __dirname) {,后面加});,然后用vm.runInThisContext执行。
  • 这五个参数就是模块的"私有空间":
    • exports:导出对象(module.exports的引用)。
    • require:当前模块的require函数。
    • module:模块对象本身。
    • __filename / __dirname:当前文件路径和目录。

示例(书中原码简化版): 你的math.js内容是:

js 复制代码
exports.add = (a, b) => a + b;

实际执行的是:

js 复制代码
(function (exports, require, module, __filename, __dirname) {
  exports.add = (a, b) => a + b;
  // ... 你的代码
});
  • .json文件:JSON.parse后赋值给module.exports。
  • .node文件:用dlopen加载C++扩展。

编译后,module.exports缓存起来,返回给调用者。

相关推荐
开开心心就好5 小时前
批量提取PDF中的图片,直接导出原图
前端·javascript·支持向量机·智能手机·pdf·html·启发式算法
Setsuna_F_Seiei7 小时前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
百万蹄蹄向前冲9 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙9 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan10 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
kyriewen11 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理11 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
IT_陈寒12 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
Blanche150012 小时前
利用 RAG 为答疑机器人扩展知识范围
前端
天若有情67312 小时前
【纯前端小工具】公历生日转农历,批量查询每年农历生日对应的公历日期(GitHub Pages在线直接用)
前端·javascript·github pages·农历转换·lunisolar·网页小工具