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
檔案包含頂層欄位"type"
,且其值為"commonjs"
時,副檔名為.js
的檔案。 -
當最近的父層
package.json
檔案不包含頂層欄位"type"
,或任何父層資料夾中都沒有package.json
時,副檔名為.js
或沒有副檔名的檔案;除非檔案包含語法,否則會發生錯誤,除非將其評估為 ES 模組。套件作者應包含"type"
欄位,即使在所有來源都是 CommonJS 的套件中也是如此。明確說明套件的type
會讓建置工具和載入器更輕鬆地判斷如何詮釋套件中的檔案。 -
具有非
.mjs
、.cjs
、.json
、.node
或.js
擴充功能的名稱檔案(當最近的父項package.json
檔案包含頂層欄位"type"
,其值為"module"
時,這些檔案僅在透過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
擴充功能保留給無法透過 require()
載入的 ECMAScript 模組。請參閱 判定模組系統 部分,以取得關於哪些檔案會被剖析為 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
是使用者的家目錄,而 $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 快捷方式 部分,以了解何時使用 exports
,何時使用 module.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
#
Module
物件,代表 Node.js 程序啟動時載入的進入腳本,或如果程序的進入點不是 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
期間搜尋的路徑,或如果 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
核心模組。