Node 到底是什么
Node.js 是一个装在你电脑上、用来运行 JavaScript 文件的程序。这是最简单的描述,而且说得没错。当你输入 node script.js 时,Node 读取这个文件,把它交给 Google 的 V8 引擎(Chrome 使用的 JavaScript 引擎)执行,同时附带一个庞大的额外 API 库,用来完成 V8 自己做不到的事情。
V8 知道怎么运行 JavaScript。但它不知道怎么打开文件、监听 TCP 套接字、启动进程或读取环境变量。这些都由 Node 用 C++ 实现,再以内置模块的形式提供给你的 JavaScript 代码。
node --version
node script.js
所以 Node 不是一门语言,也不是一个框架。它是一个运行时:V8 加上标准库,加上模块系统,再加上事件循环。全部就是这些。
第一个脚本
任何 .js 文件都是合法的 Node 程序。不需要样板代码,也不需要 main 函数:
console.log 的用法和在浏览器里一样。模板字符串、Date、数组、Promise,你已经熟悉的所有语言特性都来自 V8,行为完全相同。在 Node 中改变的,是语言之外可用的东西。
只在 Node 中存在的全局对象
浏览器有 window、document、localStorage、fetch。Node 有另一套全局对象,是围绕服务器端运行时设计的:
process代表正在运行的 Node 进程。它包含环境变量(process.env)、命令行参数(process.argv),以及退出进程的方法(process.exit(1))。__filename和__dirname给出当前文件及其所在文件夹的绝对路径。(在 ES 模块中它们不存在,要改用import.meta.url。)global是顶层对象,相当于 Node 里的window。
这里没有 document,也没有 window。如果你尝试使用它们,会得到 ReferenceError。这通常是第一个信号,说明某个库是为浏览器编写的,无法直接在 Node 中运行。
命令行参数和环境变量
Node 的很大一部分用途,比如命令行工具、构建脚本和服务器,都需要读取参数和环境变量。两者都在 process 上:
process.argv 是一个数组:前两项是 Node 可执行文件的路径和脚本路径,所以真正的参数从索引 2 开始。process.env 是一个存放环境变量的普通对象,从中读取 NODE_ENV、PORT 或 API 密钥是标准做法。
内置模块
Node 自带一个标准库,通过 require(CommonJS)或 import(ESM)使用。模块名以 node: 开头,明确表示它们是内置的:
最常用的几个:
node:fs:读写文件。需要 async/await 版本时用node:fs/promises。node:path:以跨平台的方式拼接、解析和拆分文件路径。node:http/node:https:搭建 HTTP 服务器和发送请求。node:url:解析和构造 URL。node:os:获取主机的信息。node:crypto:哈希、随机字节和加密。
这些模块不需要安装,它们随 Node 一起提供。其他的一切都来自 npm。
事件循环简介
Node 在单线程上运行你的 JavaScript,却能同时处理很多事情。诀窍就是事件循环。当你调用异步操作时,比如读取文件、发送 HTTP 请求或设置定时器,Node 会把实际的工作交给操作系统(或它自己的线程池),然后继续执行。工作完成后,回调会进入队列,等当前代码执行完毕,事件循环再把它取出来。
打印顺序是 1、4、2、3。同步代码最先运行,然后是微任务(已解决的 Promise),最后是定时器。这就是为什么一个耗费 CPU 的慢循环会阻塞整个服务器:执行你的代码的只有一个线程。Node 的并发针对的是 I/O,而不是计算。
一个迷你 HTTP 服务器
学了这么多,回报就是:一个能用的 Web 服务器只需要几行代码:
没有框架,也没有依赖。createServer 接收一个函数,每个请求都会运行它;listen 让事件循环开始处理传入的连接。真实的应用会在这之上使用 Express 或 Fastify,但底层还是同一个内置的 http 模块。
Node 与浏览器
有必要明确列出哪些能通用、哪些不能:
| 两者都可用 | 仅 Node | 仅浏览器 |
|---|---|---|
语言特性(类、Promise、async/await) | fs、http、process、__dirname | window、document、DOM |
console.log | CommonJS require / Node 特有的 ESM 细节 | localStorage、sessionStorage |
fetch(Node 18+) | 访问文件系统和网络套接字 | 用户事件、渲染 |
setTimeout、setInterval | 子进程、流 | History API、navigator |
现代 Node 已经引入了好几个浏览器 API,比如 fetch、URL、AbortController、structuredClone,所以两者的差距比过去小了。但 DOM 不会来到 Node,文件系统也不会来到浏览器。
Node、Deno 与 Bun
Node 是默认选择,但已经不是唯一的 JavaScript 运行时。Deno 和 Bun 是它的替代品:Deno 出自 Node 的原作者,Bun 来自一个专注于速度的新团队。它们运行 JavaScript(也原生支持 TypeScript),内置测试运行器和打包器等工具,在模块处理、权限和包安装方面与 Node 有所不同。
对于学习 JavaScript 来说,文档、教程和招聘岗位仍然集中在 Node 上。事件循环、模块、内置 API 这些概念几乎可以完全迁移到其他运行时。先学 Node,等项目需要时再去了解其他运行时。
快速运行脚本
开发过程中实际执行代码的几种方式:
# Run a file
node script.js
# Run a one-liner
node -e "console.log(2 ** 10)"
# Open the REPL (interactive prompt)
node
# Watch a file and re-run on save (Node 18.11+)
node --watch script.js
REPL 是一个很好用的草稿本,想看看某个方法返回什么时,不用新建文件。--watch 在开发时很方便:一保存,Node 就会自动重新运行你的脚本。
下一章:捕获错误
运行代码是一回事,出问题时如何处理是另一回事。读取文件会失败,HTTP 请求会超时,JSON 解析会抛出异常。下一章讲 try/catch、错误类型,以及处理各种故障的常用模式。在 Node 程序里,迟早每样东西都会出故障。
常见问题
Node.js 运行时是什么?
Node.js 是一个在浏览器之外运行 JavaScript 的程序。它把 Google 的 V8 引擎(Chrome 里运行 JS 的就是它)和一层 C++ 代码打包在一起,这层代码提供了 V8 本身没有的 API:文件系统访问、网络、进程和定时器。正是这个组合,让你能用 JavaScript 编写服务器、命令行工具和构建工具。
Node 和浏览器有什么区别?
两者都运行 JavaScript,但语言之外的 API 不同。浏览器给你 window、document 和 DOM。Node 给你 process、fs、http、__dirname,以及 CommonJS/ESM 模块加载。Node 里没有 DOM,浏览器里没有文件系统:语言是共通的,平台不是。
Node.js 是框架还是运行时?
是运行时。Node 本身不规定任何应用结构,它只负责执行 JavaScript 并提供 API。Express、Next.js、NestJS 这样的框架都构建在 Node 之上。Deno 和 Bun 是另外两个 JavaScript 运行时,它们和 Node 竞争,但扮演的是同样的角色。
Node 中的事件循环是什么?
事件循环让 Node 能在单线程上同时处理很多事情。当你调用异步操作时,比如读取文件、发送 HTTP 请求或 setTimeout,Node 会把工作交给系统,然后继续往下执行。工作完成后,它的回调会进入队列,由事件循环取出执行。所以 fs.readFile 不会阻塞程序的其余部分。