Node.js v21.7.2 文档
- Node.js v21.7.2
- ► 目录
-
► 索引
- 断言测试
- 异步上下文跟踪
- 异步钩子
- 缓冲区
- C++ 附加模块
- 使用 Node-API 的 C/C++ 附加模块
- C++ 嵌入器 API
- 子进程
- 集群
- 命令行选项
- 控制台
- Corepack
- 加密
- 调试器
- 已弃用的 API
- 诊断通道
- DNS
- 域
- 错误
- 事件
- 文件系统
- 全局变量
- HTTP
- HTTP/2
- HTTPS
- 检查器
- 国际化
- 模块:CommonJS 模块
- 模块:ECMAScript 模块
- 模块:
node:module
API - 模块:包
- 网络
- 操作系统
- 路径
- 性能钩子
- 权限
- 进程
- Punycode
- 查询字符串
- 读取行
- REPL
- 报告
- 单一可执行应用程序
- 流
- 字符串解码器
- 测试运行器
- 计时器
- TLS/SSL
- 跟踪事件
- TTY
- UDP/数据报
- URL
- 实用程序
- V8
- VM
- WASI
- Web Crypto API
- Web Streams API
- 工作线程
- Zlib
- ► 其他版本
- ► 选项
模块:CommonJS 模块#
CommonJS 模块是 Node.js 打包 JavaScript 代码的原始方式。Node.js 还支持浏览器和其他 JavaScript 运行时使用的 ECMAScript 模块 标准。
在 Node.js 中,每个文件都被视为一个单独的模块。例如,考虑一个名为 foo.js
的文件
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
在第一行,foo.js
加载与 foo.js
在同一目录中的模块 circle.js
。
以下是 circle.js
的内容
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
模块 circle.js
导出了 area()
和 circumference()
函数。通过在特殊的 exports
对象上指定附加属性,函数和对象被添加到模块的根目录。
模块的局部变量将是私有的,因为模块被 Node.js 包装在一个函数中(参见 模块包装器)。在这个例子中,变量 PI
对 circle.js
是私有的。
module.exports
属性可以被分配一个新值(例如函数或对象)。
下面,bar.js
使用 square
模块,该模块导出一个 Square 类
const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`The area of mySquare is ${mySquare.area()}`);
square
模块在 square.js
中定义
// Assigning to exports will not modify module, must use module.exports
module.exports = class Square {
constructor(width) {
this.width = width;
}
area() {
return this.width ** 2;
}
};
CommonJS 模块系统在 module
核心模块 中实现。
启用#
Node.js 有两个模块系统:CommonJS 模块和 ECMAScript 模块。
默认情况下,Node.js 将以下内容视为 CommonJS 模块
-
具有
.cjs
扩展名的文件; -
当最近的父
package.json
文件包含一个值为"commonjs"
的顶层字段"type"
时,具有.js
扩展名的文件。 -
扩展名为
.js
或没有扩展名的文件,当最近的父级package.json
文件不包含顶层字段"type"
或任何父文件夹中没有package.json
文件时,除非该文件包含除非作为 ES 模块进行评估才会出错的语法。包作者应该包含"type"
字段,即使在所有源代码都是 CommonJS 的包中也是如此。明确指定包的type
将使构建工具和加载器更容易确定如何解释包中的文件。 -
扩展名不是
.mjs
、.cjs
、.json
、.node
或.js
的文件(当最近的父级package.json
文件包含一个值为"module"
的顶层字段"type"
时,这些文件只有在通过require()
包含时才会被识别为 CommonJS 模块,而不是在用作程序的命令行入口点时)。
有关更多详细信息,请参阅 确定模块系统。
调用 require()
始终使用 CommonJS 模块加载器。调用 import()
始终使用 ECMAScript 模块加载器。
访问主模块#
当直接从 Node.js 运行文件时,require.main
被设置为其 module
。这意味着可以通过测试 require.main === module
来确定文件是否已直接运行。
对于文件 foo.js
,如果通过 node foo.js
运行,则为 true
,但如果通过 require('./foo')
运行,则为 false
。
当入口点不是 CommonJS 模块时,require.main
为 undefined
,并且主模块无法访问。
包管理器提示#
Node.js require()
函数的语义被设计得足够通用,以支持合理的目录结构。包管理器程序(如 dpkg
、rpm
和 npm
)有望能够从 Node.js 模块构建本机包,而无需修改。
下面我们给出一个建议的目录结构,它可以工作
假设我们希望将 /usr/lib/node/<some-package>/<some-version>
中的文件夹保存特定版本的包的内容。
包可以相互依赖。为了安装包 foo
,可能需要安装特定版本的包 bar
。bar
包本身可能也有依赖项,在某些情况下,这些依赖项甚至可能发生冲突或形成循环依赖项。
由于 Node.js 会查找它加载的任何模块的 `realpath`(即解析符号链接),然后在 `node_modules` 文件夹中查找它们的依赖项,因此可以使用以下架构解决这种情况。
/usr/lib/node/foo/1.2.3/
:`foo` 包的版本 1.2.3 的内容。/usr/lib/node/bar/4.3.2/
:`foo` 依赖的 `bar` 包的内容。/usr/lib/node/foo/1.2.3/node_modules/bar
:指向/usr/lib/node/bar/4.3.2/
的符号链接。/usr/lib/node/bar/4.3.2/node_modules/*
:指向 `bar` 依赖的包的符号链接。
因此,即使遇到循环或存在依赖冲突,每个模块都能够获得它可以使用版本的依赖项。
当 `foo` 包中的代码执行 require('bar')
时,它将获取链接到 /usr/lib/node/foo/1.2.3/node_modules/bar
的版本。然后,当 `bar` 包中的代码调用 require('quux')
时,它将获取链接到 /usr/lib/node/bar/4.3.2/node_modules/quux
的版本。
此外,为了使模块查找过程更加优化,我们可以将包放在 /usr/lib/node_modules/<name>/<version>
中,而不是直接放在 /usr/lib/node
中。然后 Node.js 将不会在 /usr/node_modules
或 /node_modules
中查找丢失的依赖项。
为了使模块可供 Node.js REPL 使用,将 /usr/lib/node_modules
文件夹添加到 $NODE_PATH
环境变量中可能会有用。由于使用 `node_modules` 文件夹的模块查找都是相对的,并且基于调用 require()
的文件的真实路径,因此包本身可以位于任何位置。
.mjs
扩展名#
由于 require()
的同步性质,无法使用它来加载 ECMAScript 模块文件。尝试这样做将抛出 ERR_REQUIRE_ESM
错误。请改用 import()
。
.mjs
扩展名保留用于 ECMAScript 模块,这些模块无法通过 require()
加载。有关哪些文件被解析为 ECMAScript 模块的更多信息,请参见 确定模块系统 部分。
总结#
要获取调用require()
时加载的确切文件名,请使用require.resolve()
函数。
将以上所有内容整合在一起,以下是require()
执行的高级伪代码算法
require(X) from module at path Y 1. If X is a core module, a. return the core module b. STOP 2. If X begins with '/' a. set Y to be the file system root 3. If X begins with './' or '/' or '../' a. LOAD_AS_FILE(Y + X) b. LOAD_AS_DIRECTORY(Y + X) c. THROW "not found" 4. If X begins with '#' a. LOAD_PACKAGE_IMPORTS(X, dirname(Y)) 5. LOAD_PACKAGE_SELF(X, dirname(Y)) 6. LOAD_NODE_MODULES(X, dirname(Y)) 7. THROW "not found" LOAD_AS_FILE(X) 1. If X is a file, load X as its file extension format. STOP 2. If X.js is a file, load X.js as JavaScript text. STOP 3. If X.json is a file, parse X.json to a JavaScript Object. STOP 4. If X.node is a file, load X.node as binary addon. STOP LOAD_INDEX(X) 1. If X/index.js is a file, load X/index.js as JavaScript text. STOP 2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP 3. If X/index.node is a file, load X/index.node as binary addon. STOP LOAD_AS_DIRECTORY(X) 1. If X/package.json is a file, a. Parse X/package.json, and look for "main" field. b. If "main" is a falsy value, GOTO 2. c. let M = X + (json main field) d. LOAD_AS_FILE(M) e. LOAD_INDEX(M) f. LOAD_INDEX(X) DEPRECATED g. THROW "not found" 2. LOAD_INDEX(X) LOAD_NODE_MODULES(X, START) 1. let DIRS = NODE_MODULES_PATHS(START) 2. for each DIR in DIRS: a. LOAD_PACKAGE_EXPORTS(X, DIR) b. LOAD_AS_FILE(DIR/X) c. LOAD_AS_DIRECTORY(DIR/X) NODE_MODULES_PATHS(START) 1. let PARTS = path split(START) 2. let I = count of PARTS - 1 3. let DIRS = [] 4. while I >= 0, a. if PARTS[I] = "node_modules" CONTINUE b. DIR = path join(PARTS[0 .. I] + "node_modules") c. DIRS = DIR + DIRS d. let I = I - 1 5. return DIRS + GLOBAL_FOLDERS LOAD_PACKAGE_IMPORTS(X, DIR) 1. Find the closest package scope SCOPE to DIR. 2. If no scope was found, return. 3. If the SCOPE/package.json "imports" is null or undefined, return. 4. let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), ["node", "require"]) defined in the ESM resolver. 5. RESOLVE_ESM_MATCH(MATCH). LOAD_PACKAGE_EXPORTS(X, DIR) 1. Try to interpret X as a combination of NAME and SUBPATH where the name may have a @scope/ prefix and the subpath begins with a slash (`/`). 2. If X does not match this pattern or DIR/NAME/package.json is not a file, return. 3. Parse DIR/NAME/package.json, and look for "exports" field. 4. If "exports" is null or undefined, return. 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, `package.json` "exports", ["node", "require"]) defined in the ESM resolver. 6. RESOLVE_ESM_MATCH(MATCH) LOAD_PACKAGE_SELF(X, DIR) 1. Find the closest package scope SCOPE to DIR. 2. If no scope was found, return. 3. If the SCOPE/package.json "exports" is null or undefined, return. 4. If the SCOPE/package.json "name" is not the first segment of X, return. 5. let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE), "." + X.slice("name".length), `package.json` "exports", ["node", "require"]) defined in the ESM resolver. 6. RESOLVE_ESM_MATCH(MATCH) RESOLVE_ESM_MATCH(MATCH) 1. let RESOLVED_PATH = fileURLToPath(MATCH) 2. If the file at RESOLVED_PATH exists, load RESOLVED_PATH as its extension format. STOP 3. THROW "not found"
缓存#
模块在第一次加载后会被缓存。这意味着(除其他事项外)每次调用require('foo')
都会返回完全相同的对象,如果它解析到相同的文件。
如果require.cache
没有被修改,多次调用require('foo')
不会导致模块代码被多次执行。这是一个重要的特性。有了它,可以返回“部分完成”的对象,从而允许加载传递依赖项,即使它们会导致循环。
要让模块多次执行代码,请导出一个函数,并调用该函数。
模块缓存注意事项#
模块根据其解析后的文件名进行缓存。由于模块可能会根据调用模块的位置解析到不同的文件名(从node_modules
文件夹加载),因此不能保证require('foo')
始终返回完全相同的对象,如果它解析到不同的文件。
此外,在不区分大小写的文件系统或操作系统上,不同的解析文件名可能指向同一个文件,但缓存仍然会将它们视为不同的模块,并多次重新加载文件。例如,require('./foo')
和require('./FOO')
返回两个不同的对象,无论./foo
和./FOO
是否为同一个文件。
核心模块#
Node.js 有几个编译到二进制文件中的模块。这些模块在本手册的其他地方有更详细的描述。
核心模块在 Node.js 源代码中定义,位于lib/
文件夹中。
核心模块可以使用node:
前缀识别,在这种情况下,它会绕过require
缓存。例如,require('node:http')
将始终返回内置的 HTTP 模块,即使require.cache
中存在同名条目。
如果将核心模块标识符传递给require()
,则始终优先加载某些核心模块。例如,require('http')
将始终返回内置的 HTTP 模块,即使存在同名文件。可以使用module.builtinModules
公开可以不使用node:
前缀加载的核心模块列表。
循环#
当存在循环require()
调用时,模块在返回时可能尚未完成执行。
考虑以下情况
a.js
:
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js
:
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js
:
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done = %j, b.done = %j', a.done, b.done);
当 main.js
加载 a.js
时,a.js
又会加载 b.js
。此时,b.js
尝试加载 a.js
。为了防止无限循环,a.js
导出对象的**未完成副本**将返回给 b.js
模块。然后 b.js
完成加载,并将它的 exports
对象提供给 a.js
模块。
当 main.js
加载完这两个模块后,它们都已完成。因此,该程序的输出将是
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done = true, b.done = true
需要仔细规划才能使循环模块依赖关系在应用程序中正常工作。
文件模块#
如果找不到确切的文件名,Node.js 将尝试加载带有以下扩展名的所需文件名:.js
、.json
,最后是 .node
。当加载具有不同扩展名的文件(例如 .cjs
)时,必须将它的完整名称(包括文件扩展名)传递给 require()
(例如 require('./file.cjs')
)。
.json
文件被解析为 JSON 文本文件,.node
文件被解释为使用 process.dlopen()
加载的已编译的附加模块。使用任何其他扩展名(或根本没有扩展名)的文件被解析为 JavaScript 文本文件。请参阅 确定模块系统 部分以了解将使用什么解析目标。
以 '/'
为前缀的所需模块是文件的绝对路径。例如,require('/home/marco/foo.js')
将加载 /home/marco/foo.js
中的文件。
以 './'
为前缀的所需模块相对于调用 require()
的文件。也就是说,circle.js
必须与 foo.js
位于同一个目录中,才能让 require('./circle')
找到它。
如果没有以 '/'
、'./'
或 '../'
开头的文件,则该模块必须是核心模块,或者从 node_modules
文件夹加载。
如果给定的路径不存在,require()
将抛出一个 MODULE_NOT_FOUND
错误。
文件夹作为模块#
有三种方法可以将文件夹作为参数传递给 require()
。
第一种是在文件夹的根目录创建一个 package.json
文件,该文件指定一个 main
模块。一个示例 package.json
文件可能如下所示
{ "name" : "some-library",
"main" : "./lib/some-library.js" }
如果这位于 ./some-library
的文件夹中,那么 require('./some-library')
将尝试加载 ./some-library/lib/some-library.js
。
如果目录中不存在 package.json
文件,或者 "main"
条目丢失或无法解析,那么 Node.js 将尝试从该目录加载 index.js
或 index.node
文件。例如,如果在前面的示例中不存在 package.json
文件,那么 require('./some-library')
将尝试加载
./some-library/index.js
./some-library/index.node
如果这些尝试失败,那么 Node.js 将报告整个模块丢失,并显示默认错误
Error: Cannot find module 'some-library'
在以上三种情况下,import('./some-library')
调用将导致 ERR_UNSUPPORTED_DIR_IMPORT
错误。使用包 子路径导出 或 子路径导入 可以提供与文件夹作为模块相同的包含组织优势,并且适用于 require
和 import
。
从 node_modules
文件夹加载#
如果传递给 require()
的模块标识符不是 核心 模块,并且不以 '/'
、'../'
或 './'
开头,那么 Node.js 将从当前模块的目录开始,并添加 /node_modules
,并尝试从该位置加载模块。Node.js 不会将 node_modules
附加到以 node_modules
结尾的路径。
如果在当前目录中找不到,它会移动到父目录,依此类推,直到到达文件系统的根目录。
例如,如果位于 '/home/ry/projects/foo.js'
的文件调用了 require('bar.js')
,那么 Node.js 将按以下顺序查找这些位置:
/home/ry/projects/node_modules/bar.js
/home/ry/node_modules/bar.js
/home/node_modules/bar.js
/node_modules/bar.js
这允许程序将它们的依赖项本地化,以避免冲突。
可以通过在模块名称后添加路径后缀来要求与模块一起分发的特定文件或子模块。例如,require('example-module/path/to/file')
将解析相对于 example-module
所在位置的 path/to/file
。后缀路径遵循相同的模块解析语义。
从全局文件夹加载#
如果 NODE_PATH
环境变量被设置为一个以冒号分隔的绝对路径列表,那么 Node.js 将在这些路径中搜索模块,如果它们在其他地方找不到。
在 Windows 上,NODE_PATH
以分号 (;
) 而不是冒号分隔。
NODE_PATH
最初是为了支持在当前 模块解析 算法定义之前从不同路径加载模块而创建的。
NODE_PATH
仍然受支持,但现在 Node.js 生态系统已经确定了定位依赖模块的约定,因此它不再那么必要。有时,依赖于 NODE_PATH
的部署在人们不知道必须设置 NODE_PATH
时会表现出令人惊讶的行为。有时模块的依赖项会发生变化,导致加载不同的版本(甚至不同的模块),因为 NODE_PATH
被搜索。
此外,Node.js 将在以下 GLOBAL_FOLDERS 列表中搜索:
- 1:
$HOME/.node_modules
- 2:
$HOME/.node_libraries
- 3:
$PREFIX/lib/node
其中 $HOME
是用户的 home 目录,$PREFIX
是 Node.js 配置的 node_prefix
。
这些主要是出于历史原因。
强烈建议将依赖项放在本地 node_modules
文件夹中。这些将加载得更快,并且更可靠。
模块包装器#
在执行模块代码之前,Node.js 会用一个类似于以下的函数包装器将其包装起来:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
通过这样做,Node.js 实现了一些事情:
- 它将顶层变量(使用
var
、const
或let
定义)限定在模块范围内,而不是全局对象范围内。 - 它有助于提供一些看起来像全局变量的变量,但实际上是特定于模块的,例如:
- 实现者可以使用
module
和exports
对象从模块导出值。 - 便捷变量
__filename
和__dirname
,分别包含模块的绝对文件名和目录路径。
- 实现者可以使用
模块作用域#
__dirname
#
当前模块的目录名称。这与path.dirname()
的__filename
相同。
示例:从/Users/mjr
运行node example.js
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
__filename
#
当前模块的文件名。这是当前模块文件的绝对路径,已解析符号链接。
对于主程序,这并不一定与命令行中使用的文件名相同。
有关当前模块的目录名称,请参见__dirname
。
示例
从/Users/mjr
运行node example.js
console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr
给定两个模块:a
和b
,其中b
是a
的依赖项,并且存在以下目录结构
/Users/mjr/app/a.js
/Users/mjr/app/node_modules/b/b.js
b.js
中对__filename
的引用将返回/Users/mjr/app/node_modules/b/b.js
,而a.js
中对__filename
的引用将返回/Users/mjr/app/a.js
。
exports
#
对module.exports
的引用,类型更短。有关何时使用exports
以及何时使用module.exports
的详细信息,请参阅有关exports 快捷方式的部分。
module
#
对当前模块的引用,请参阅有关module
对象的部分。特别是,module.exports
用于定义模块导出的内容,并通过require()
提供。
require(id)
#
用于导入模块、JSON
和本地文件。模块可以从 node_modules
导入。本地模块和 JSON 文件可以使用相对路径(例如 ./
、./foo
、./bar/baz
、../foo
)导入,这些路径将相对于由 __dirname
(如果定义)或当前工作目录命名的目录解析。POSIX 样式的相对路径以与操作系统无关的方式解析,这意味着上述示例在 Windows 上的工作方式与在 Unix 系统上的工作方式相同。
// Importing a local module with a path relative to the `__dirname` or current
// working directory. (On Windows, this would resolve to .\path\myLocalModule.)
const myLocalModule = require('./path/myLocalModule');
// Importing a JSON file:
const jsonData = require('./path/filename.json');
// Importing a module from node_modules or Node.js built-in module:
const crypto = require('node:crypto');
require.cache
#
模块在被需要时被缓存到这个对象中。通过从这个对象中删除一个键值,下一个 require
将重新加载模块。这并不适用于 原生插件,因为重新加载它们会导致错误。
添加或替换条目也是可能的。在检查内置模块之前会检查此缓存,如果缓存中添加了与内置模块匹配的名称,则只有 node:
前缀的 require 调用将接收内置模块。谨慎使用!
const assert = require('node:assert');
const realFs = require('node:fs');
const fakeFs = {};
require.cache.fs = { exports: fakeFs };
assert.strictEqual(require('fs'), fakeFs);
assert.strictEqual(require('node:fs'), realFs);
require.extensions
#
指示 require
如何处理某些文件扩展名。
将扩展名为 .sjs
的文件作为 .js
处理
require.extensions['.sjs'] = require.extensions['.js'];
已弃用。 过去,此列表用于通过按需编译将非 JavaScript 模块加载到 Node.js 中。但是,在实践中,有更好的方法可以做到这一点,例如通过其他 Node.js 程序加载模块,或提前将它们编译为 JavaScript。
避免使用 require.extensions
。使用可能会导致细微的错误,并且随着每个注册的扩展名的增加,解析扩展名会变得越来越慢。
require.main
#
表示在 Node.js 进程启动时加载的入口脚本的 Module
对象,如果程序的入口点不是 CommonJS 模块,则为 undefined
。请参阅 "访问主模块"。
在 entry.js
脚本中
console.log(require.main);
node entry.js
Module {
id: '.',
path: '/absolute/path/to',
exports: {},
filename: '/absolute/path/to/entry.js',
loaded: false,
children: [],
paths:
[ '/absolute/path/to/node_modules',
'/absolute/path/node_modules',
'/absolute/node_modules',
'/node_modules' ] }
require.resolve(request[, options])
#
request
<string> 要解析的模块路径。options
<Object>paths
<string[]> 用于解析模块位置的路径。如果存在,则使用这些路径而不是默认的解析路径,但 GLOBAL_FOLDERS(如$HOME/.node_modules
)除外,这些路径始终包含在内。每个路径都用作模块解析算法的起点,这意味着从该位置检查node_modules
层次结构。
- 返回:<string>
使用内部 require()
机制查找模块的位置,但不是加载模块,而是返回解析后的文件名。
如果找不到模块,则会抛出 MODULE_NOT_FOUND
错误。
require.resolve.paths(request)
#
request
<string> 要检索其查找路径的模块路径。- 返回:<string[]> | <null>
返回一个数组,其中包含在解析 request
期间搜索的路径,如果 request
字符串引用核心模块(例如 http
或 fs
),则返回 null
。
module
对象#
在每个模块中,module
自由变量都是对表示当前模块的对象的引用。为了方便起见,module.exports
也可以通过 exports
模块全局变量访问。module
实际上不是全局变量,而是每个模块的局部变量。
module.children
#
此模块对象是此模块首次需要的。
module.exports
#
module.exports
对象由 Module
系统创建。有时这不可接受;许多人希望他们的模块是某个类的实例。为此,将所需的导出对象分配给 module.exports
。将所需对象分配给 exports
将只是重新绑定本地 exports
变量,这可能不是想要的。
例如,假设我们正在制作一个名为 a.js
的模块
const EventEmitter = require('node:events');
module.exports = new EventEmitter();
// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
module.exports.emit('ready');
}, 1000);
然后在另一个文件中我们可以这样做
const a = require('./a');
a.on('ready', () => {
console.log('module "a" is ready');
});
对 module.exports
的赋值必须立即完成。它不能在任何回调中完成。这不起作用
x.js
:
setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);
y.js
:
const x = require('./x');
console.log(x.a);
exports
快捷方式#
exports
变量在模块的文件级范围内可用,并在模块评估之前被分配 module.exports
的值。
它允许使用快捷方式,因此 module.exports.f = ...
可以更简洁地写成 exports.f = ...
。但是,请注意,与任何变量一样,如果为 exports
分配了新值,它将不再绑定到 module.exports
module.exports.hello = true; // Exported from require of module
exports = { hello: false }; // Not exported, only available in the module
当 module.exports
属性被完全替换为一个新对象时,通常也会重新分配 exports
module.exports = exports = function Constructor() {
// ... etc.
};
为了说明这种行为,想象一下 require()
的这个假设实现,它与 require()
实际执行的操作非常相似
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Module code here. In this example, define a function.
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
// At this point, the module will now export someFunc, instead of the
// default object.
})(module, module.exports);
return module.exports;
}
module.filename
#
模块的完全解析文件名。
module.id
#
模块的标识符。通常情况下,这是完全解析的文件名。
module.isPreloading
#
- 类型:<boolean> 如果模块在 Node.js 预加载阶段运行,则为
true
。
module.loaded
#
模块是否已完成加载或正在加载中。
module.parent
#
第一个需要此模块的模块,如果当前模块是当前进程的入口点,则为 null
,如果模块是由非 CommonJS 模块(例如:REPL 或 import
)加载的,则为 undefined
。
module.path
#
模块的目录名称。这通常与 module.id
的 path.dirname()
相同。
module.paths
#
模块的搜索路径。
module.require(id)
#
module.require()
方法提供了一种加载模块的方式,就像从原始模块调用 require()
一样。
为了做到这一点,需要获取对 module
对象的引用。由于 require()
返回 module.exports
,并且 module
通常只在特定模块的代码中可用,因此必须显式导出它才能使用。
Module
对象#
本节已移至 模块: module
核心模块。
源映射 v3 支持#
本节已移至 模块:module
核心模块。