الوحدات (Modules) في Node.js
مؤشر الاستقرار: 2 - مستقر
يعامل كل ملف في نظام الوحدات في بيئة 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
الوحدة circle.js
الموجودة في نفس المجلد مع foo.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 (انظر مُغلِّف الوحدات module wrapper). في هذا المثال، يكون المتغير 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;
}
};
ويتحقق نظام الوحدات في الوحدة require('module').
الوصول إلى الوحدة الرئيسية
عند تشغيل ملف مباشرة من Node.js، تُضبط الخاصية require.main
إلى وحدتها module
. وهذا يعني أنه من الممكن تحديد ما إذا كان قد جرى تشغيل ملفٍ ما مباشرة عن طريق اختبار require.main === module
.
بالنسبة لملف foo.js
، ستكون قيمتها true
إذا نُفِذت عبر node foo.js
لكن تكون false
إذا نُفِذت عبر require('./foo').
ولمّا كانت الوحدة module
توفر الخاصية filename
(عادة ما تكافئ __filename
) فيمكن الحصول على نقطة الدخول من التطبيق الحالي عن طريق فحص require.main.filename
.
Addenda: نصائح عن مدير الحِزم
صُممت دلالات الدالة require()
في Node.js لتكون عامة بما يكفي لدعم عدد من هياكل المجلدات المعقولة. تأمل تطبيقات إدارة حِزم البرامج مثل 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/
: محتويات حزمةbar
التي تعتمد عليها حزمةfoo
. //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()
، يمكن أن تكون الحزم نفسها في أي مكان.
خلاصة الكلام السابق
تُستخدم الدالة require.resolve()
للحصول على اسم الملف الصحيح الذي سيُحمَّل عند استدعاء require()
.
بتجميع كل ما سبق، فيما يلي خوارزمية رفيعة المستوى في شكل شيفرة وهمية (pseudocode) عما يجريه require.resolve()
:
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 filesystem root
3. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. 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. let M = X + (json main field)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M)
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. 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 = [GLOBAL_FOLDERS]
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
5. return DIRS
التخزين المؤقت (Caching)
تُخزَّن الوحدات مؤقتًا بعد تحميلها أول مرة. وهذا يعني (من ضمن أمور أخرى) أن كل استدعاء للتابع require('foo')
سوف يعيد بالضبط الكائن نفسه إذ كان يحل نفس الملف.
قد لا تسبب عدة استدعاءات للتابع require('foo')
تنفيذ شيفرة الوحدة عدة مرات. وهي ميزة هامة. مع ذلك، يمكن إعادة الكائنات "المُنفَّذة جزئيًا"، مما يسمح بتحميل الاعتماديات الانتقالية حتى عندما تتسبب في دورات.
لتنفيذ التعليمات البرمجية لوحدة عدة مرات يجب تصدير دالة واستدعاء هذه الدالة.
محاذير التخزين المؤقت للوحدات
تُخزين الوحدات مؤقتاً على أساس حل اسم الملف. لما كان حل الوحدات إلى اسم ملف مختلف على أساس موقع الوحدة المُستدعِية (تحميل من مجلدات node_modules
)، فإنه لا يضمن أن يُعيد التابع require('foo')
دائمًا نفس الكائن بالضبط، إذا كان يحل لملفات مختلفة.
بالإضافة إلى ذلك، في أنظمة الملفات أو أنظمة التشغيل الحساسة لحالة الأحرف، يمكن أن تشير الأسماء المحلولة المختلفة إلى نفس الملف، ولكن ذاكرة التخزين المؤقت ستستمر في التعامل معها كوحدات مختلفة، وستُحمل الملف عدة مرات. على سبيل المثال، يعيد كلٌ من require('./foo')
و require('./FOO')
كائنين مختلفين، بغض النظر عن ما إذا كان ./foo
و ./FOO
هما نفس الملف.
الوحدات الأساسية
تتمتع بيئة Node.js بالعديد من الوحدات المترجمة إلى النظام الثنائي. وتوصف هذه الوحدات بمزيد من التفصيل في مكان آخر من هذا التوثيق.
وتُعرَّف الوحدات الأساسية داخل مصدر Node.js وهي موجودة في المجلد lib/
.
وتُحمَّل الوحدات الأساسية دائمًا بشكل تفضيلي إذا مُرر المعرف الخاص بها إلى require()
. على سبيل المثال، يعيد require('http')
دائماً وحدة HTTP المُضمَّنة، حتى إذا كان هناك ملف بهذا الاسم.
الدورات
عندما تكون هناك استدعاءات 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
. من أجل منع حلقة لا نهائية، تُعاد نسخة غير مكتملة لكائن exports
الخاص بالوحدة 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
.
تُفسر ملفات .js
على أنها ملفات JavaScript نصية، وثُحلَّل الملفات .json
كملفات JSON نصية. وتُفسَّر ملفات .node
كوحدات مُترجَمة مُلحَقة ومُحمَّلة مع dlopen
.
الوحدة المُستدعاة المسبوقة بالعلامة '/
' تمثل المسار المطلق للملف. على سبيل المثال، سيُحمِّل require('/home/marco/foo.js')
الملف /home/marco/foo.js
.
تمثل الوحدة المُستدعاة المسبوقة بالعلامة '/.
' المسار النسبي للملف المُستدعي require()
. فيجب أن يكون circle.js
في نفس المجلد مع foo.js
حتى يتمكن require('./circle')
من العثور عليه.
بدون البادئة '/
' أو './
' أو '../
' للإشارة إلى ملف، يجب أن تكون الوحدة وحدةً أساسيةً أو أن تكون مُحملة من مجلد node_modules
.
إذا كان المسار غير موجود، سوف يلقي require()
الخطأ Error
مع رمز الخاصية code
بالقيمة '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
.
هذا هو مدى تمييز Node.js لملفات package.json
.
إذا كان الملف المحدد بواسطة المُدخَل الرئيسي 'main
' هو package.json
مفقود ولا يمكن أن يُستبين، سيُبلِغ Node.js أن الوحدة كاملةً مفقودة بسبب الخطأ الافتراضي:
Error: Cannot find module 'some-library'
إذا لم يكن هناك أي ملف package.json
موجودًا في المجلد، ستحاول Node.js تحميل ملف index.js
أو index.node
من داخل هذا المجلد. على سبيل المثال، إذا لم يكن هناك أي ملف package.json
في المثال أعلاه، سيحاول require('./some-library')
عندئذ تحميل:
./some-library/index.js
./some-library/index.node
التحميل من مجلدات node_modules
إذا كان مُعرِّف الوحدة المُمررة إلى require()
ليس وحدة نواة core
، ولا يبدأ مع '/
' أو ../
'' أو './
'، ثم تبدأ Node.js من المجلد الأصل للوحدة الحالية، وتضيف /node_modules
، وتحاول تحميل الوحدة من ذلك الموقع. لن تلحق Node المجلد 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')
إلى الملف path/to/file
بالنسبة إلى حيث يوجد example-module
. ويتبع المسار الملحق نفس دلالات حل الوحدات.
التحميل من المجلدات العامة
إذا ضُبط متغير البيئة NODE_PATH
على قائمة مسارات مطلقة محددة بنقطتين رأسيتين ':
'، ستبحث Node.js في تلك المسارات عن وحدات إذا لم تكن موجودة في أماكن أخرى.
في ويندوز، يُحدد 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_prefix
المُكوّنة لبيئة Node.js.
وهذا في الغالب لأسباب تاريخية.
يُنصح بقوة بوضع الاعتماديات في مجلد node_modules
المحلي. فستُحمَّل أسرع وتكون أكثر موثوقية.
مُغلِّف الوحدات (The module wrapper)
قبل تنفيذ التعليمات البرمجية في الوحدة، ستُغلِّفها Node.js بمغلف دوال يشبه ما يلي:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
عند القيام بذلك، تحقق Node.js بضعة أشياء:
- تحفظ المتغيرات ذات المستوى الأعلى (المُعرَّفة مع
var
أوconst
أوlet
) في نطاق الوحدة بدلاً من نطاق الكائن العمومي. - ويساعد على توفير بعض متغيرات البحث العامة والتي تكون في الواقع مخصصة بالوحدة، مثل:
- كائنات
module
وexports
التي يمكن للمُنفِّذ استخدامها لتصدير القيم من الوحدة. - متغيرات المواءمة
__filename
و __dirname
، التي تحتوي على اسم ملف الوحدة ومسار مجلده المطلقَين.
- كائنات
نطاق الوحدة النمطية
__dirname
أُضيف مع الإصدار: v0.1.27.
- من النوع <string>.
اسم مجلد الوحدة الحالية. وهو مثل path.dirname()
في __filename
.
مثال: تشغيل node example.js
من /Users/mjr
.
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
__filename
أُضيف مع الإصدار: v0.0.1.
- من النوع <string>.
اسم ملف الوحدة الحالية. وهو استبيان المسار المطلق لملف الوحدة الحالية.
ليس بالضرورة لبرنامج رئيسي أن يكون له نفس اسم الملف المستخدم في سطر الأوامر.
راجع __dirname
لاسم مجلد الوحدة الحالية.
أمثلة: تشغيل node example.js
من /Users/mjr
.
بالنظر إلى الوحدتين: a
و b
حيث b
هو اعتمادية للوحدة a
وتوجد بنية المجلد:
-
/Users/mjr/app/a.js
-
/Users/mjr/app/node_modules/b/b.js
المراجع إلى __filename
داخل b.js
ستُعيد /Users/mjr/app/node_modules/b/b.js
في حين تعيد المراجع إلى __filename
داخل a.js
القيمة /Users/mjr/app/a
.
exports
أُضيف مع الإصدار: v0.1.12.
هو مرجع إلى module.exports
أقصر في الكتابة. راجع قسم exports shortcut
للحصول على تفاصيل حول متى يمكن استخدام exports
ومتى تُستخدم module.exports.
module
أُضيف مع الإصدار: v0.1.16.
- من النوع <Object>.
هو مرجع إلى الوحدة الحالية، راجع قسم كائن module object
. ولا سيما module.exports
لتحديد ما تصدره الوحدة وتتيحه من خلال require()
.
require()
أُضيف مع الإصدار: v0.1.13.
- من النوع <Function>.
لطلب الوحدات.
require.cache
أُضيف مع الإصدار: v0.3.0.
- من النوع <Object>.
تُخزَّن الوحدات مؤقتًا في هذا الكائن عندما تكون مطلوبة. عند حذف قيمة مفتاحية من هذا الكائن، سيُعيد require التالي تحميل الوحدة. علما بأن هذا لا ينطبق على addons الأصلية، إذ تُنتج إعادة التحميل خطًأ. require.extensions أضيفت مع الإصدار: v0.3.0، وأُهمِلت مع الإصدار: v0.10.6. مؤشر الاستقرار: 0 - مُهمَل • من النوع <Object>. إرشاد require حول كيفية التعامل مع بعض امتدادات الملفات. معالجة الملفات بالامتداد .sjs مثل .js:
مهملة في الماضي، استخدمت هذه القائمة لتحميل الوحدات غير JavaScript إلى Node.js بترجمتها حسب الطلب. ومع ذلك، في الممارسة العملية، هناك سبل أفضل للقيام بذلك، مثل تحميل الوحدات عن طريق بعض برامج Node.js الأخرى، أو ترجمتها إلى JavaScript قبل وقتها. لما كان نظام الوحدات مغلقًا، فربما لم تُلغى هذه الميزة أبدًا. ومع ذلك، فقد يكون بها أخطاء خفية وتعقيدات من الأفضل تركها بلا مساس. علما بأن عدد عمليات نظام الملفات التي يجب على نظام الوحدات أداءها من أجل حل جملة require(...) إلى اسم ملف يقاس خطيًا مع عدد الملحقات المسجلة. وبعبارة أخرى، إضافة الملحقات يبطئ من تحميل الوحدة وينبغي تجنبها. require.main أُضيفت مع الإصدار: v0.1.17. • من النوع <Object>. كائن الوحدة Module الذي يمثل سكربت الإدخال المُحمَّل عند بدء عملية Node.js. راجع "الوصول إلى الوحدة الرئيسية". في سكربت entry.js:
require.resolve(request[, options]) الإصدار التغييرات v8.9.0 دعم خيار paths الآن. v0.3.0 أُضيف مع الإصدار: v0.3.0.
• request من النوع <string>: مسار الوحدة المراد حله. • options من النوع <Object> ◦ paths من النوع <string[]>: المسارات المراد حل موقع الوحدة من عندها. إذا كانت هذه المسارات موجودة، تستخدم بدلاً من مسارات الحل الافتراضية، باستثناء GLOBAL_FOLDERS مثل $HOME/.node_modules المُتضمَّن دائماً. لاحظ أن كل من هذه المسارات تستخدم كنقطة انطلاق خوارزمية وحدة الحل، وهذا يعني أن التحقق من التسلسل الهرمي للمجلد node_modules يبدأ من هذا الموقع. • القيمة المُعادة: من النوع <string>.
استخدام آلات require() الداخلية للبحث عن موقع وحدةٍ ما، ولكن بدلاً من تحميل الوحدة، يُعاد فقط اسم الملف المحلول. require.resolve.paths(request) أُضيف مع الإصدار: v8.9.0.
• request من النوع <string>: مسار الوحدة التي يُسترد مسارات البحث الخاصة بها. • القيمة المُعادة: <string[]> | <null>
إعادة مصفوفة تحتوي على المسارات التي بُحِثَ عنها خلال حل request أو null إذا كانت سلسلة request تشير إلى وحدة نواة، على سبيل المثال http أو fs. كائن module أُضيف مع الإصدار: v0.1.16. • من النوع <Object>. في كل وحدة، يكون متغير module الحر مرجعًا للكائن الذي يمثل الوحدة الحالية. لمزيد من المواءمة، يمكن الوصول إلى module.exports أيضا عبر وحدة exports العامة. module ليست في الواقع عامة ولكنها بالأحرى محلية لكل وحدة. module.children أُضيف مع الإصدار: v0.1.16.
• من النوع <module[]>
كائنات الوحدة المطلوبة من قِبلها. module.exports أُضيف مع الإصدار: v0.1.16. • من النوع <Object>. يُنشِأ نظامُ Module كائنَ module.exports. في بعض الأحيان يكون هذا الأمر غير مقبول؛ يريد الكثير أن تكون وحدتهم مثيلة لصنف ما. للقيام بذلك، يجب تعيين الكائن المطلوب تصديره إلى module.exports. لاحظ أن تعيين الكائن المطلوب إلى exports سيربط ببساطة متغيرexports المحلي، وهو على الأرجح ليس هو المطلوب. على سبيل المثال لنفترض عمل وحدة تسمى a.js:
ثم في ملف آخر يمكننا أن نعمل:
علما بأن يجب أن تكون الإحالة إلى module.exports على الفور. ولا يمكن القيام بذلك في أي استدعاءات. فهذا لا يعمل: x.js:
y.js:
اختصار الصادرات أُضيف مع الإصدار: v0.1.16. متغير exports متاح داخل نطاق مستوى الملف الوحدة، ويُعيَّن بقيمة module.exports قبل تقييم الوحدة. أنها تسمح باختصار، حتى أنه يمكن كتابة module.exports.f = ... بطريقة أكثر وضوحًا مثل exports.f = .... ومع ذلك، يجب الانتباه أنه مثل أي متغير، إذا عُيّنت exports بقيمة جديدة، فإنها لن تظل مرتبطة مع module.exports:
عندما يحل محل الخاصية module.exports كائن جديد بشكلٍ تام، فمن الشائع أن أيضا إعادة تعيين exports:
لتوضيح السلوك، تخيل هذا التنفيذ الافتراضي للتابع require()، والذي يماثل تماما ما يقوم به فعلا require():
module.filename أُضيف مع الإصدار: v0.1.16.
• من النوع <string>.
اسم الملف المحلول بالكامل للوحدة. module.id أُضيف مع الإصدار: v0.1.16.
• من النوع <string>.
معرف الوحدة. عادة هو اسم الملف المحلول بالكامل. module.loaded أُضيف مع الإصدار: v0.1.16.
• من النوع <boolean>
ما إذا كان قد انتهى تحميل الوحدة أم لا، أو لا يزال في عملية التحميل. module.parent أُضيف مع الإصدار: v0.1.16.
• من النوع <module>
الوحدة التي تتطلبها أولاً. module.paths أُضيف مع الإصدار: v0.4.0.
• من النوع <string[]>
مسارات البحث عن الوحدة. module.require(id) أُضيف مع الإصدار: v0.5.1.
• id من النوع <string> • القيمة المُعادة: <Object> من النوع module.exports من الوحدة المحلولة.
يوفر التابع module.require طريقة لتحميل الوحدة كما لو اُستدعِي require() من الوحدة الأصلية. للقيام بذلك، من الضروري الحصول على مرجع إلى كائن module. لما كان require() يعيد module.exports، وتتوفر module عادة في التعليمات البرمجية فقط لوحدة معينة، فإنه يجب تصديرها صراحة من أجل استخدامها. كائن Module أُضيف مع الإصدار: v0.3.7. • من النوع <Object>. يوفر توابع خدمة عامة عند التعامل مع نسخ من الوحدة Module –غالباً ما يُرى متغير module في وحدات الملف. الوصول إليها عن طريق require('module'). module.builtinModules أُضيف مع الإصدار: v9.3.0.
• من النوع <string[]>.
قائمة بأسماء كافة الوحدات التي توفرها Node.js. يمكن استخدامها للتحقق من إذا كان الوحدة تُصان من قِبَل طرف ثالث أو لا. لاحظ أن الوحدة module في هذا السياق ليست نفس الكائن الذي توفره وحدة التغليف module wrapper. للوصول إليها، يجب طلب الوحدة Module: const builtin = require('module').builtinModules;