الوحدات (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) {
// تقع شيفرة الوحدة في الواقع هنا
});

عند القيام بذلك، تحقق Node.js بضعة أشياء:

  • تحفظ المتغيرات ذات المستوى الأعلى (المُعرَّفة مع var أو const أو let) في نطاق الوحدة بدلاً من نطاق الكائن العمومي.
  • ويساعد على توفير بعض متغيرات البحث العامة والتي تكون في الواقع مخصصة بالوحدة، مثل:
    • كائنات module و exports التي يمكن للمُنفِّذ استخدامها لتصدير القيم من الوحدة.
    • متغيرات المواءمة ‎__filename و ‎‎__dirname، التي تحتوي على اسم ملف الوحدة ومسار مجلده المطلقَين.

نطاق الوحدة النمطية

‎‎__dirname

أُضيف مع الإصدار: v0.1.27.

اسم مجلد الوحدة الحالية. وهو مثل path.dirname()‎ في ‎__filename.

مثال: تشغيل node example.js من ‎/Users/mjr.

console.log(__dirname);
// طباعة: /Users/mjr
console.log(path.dirname(__filename));
// طباعة: /Users/mjr

‎__filename

أُضيف مع الإصدار: v0.0.1.

اسم ملف الوحدة الحالية. وهو استبيان المسار المطلق لملف الوحدة الحالية.

ليس بالضرورة لبرنامج رئيسي أن يكون له نفس اسم الملف المستخدم في سطر الأوامر.

راجع ‎‎__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.

هو مرجع إلى الوحدة الحالية، راجع قسم كائن module object. ولا سيما module.exports لتحديد ما تصدره الوحدة وتتيحه من خلال require()‎.

require()‎

أُضيف مع الإصدار: v0.1.13.

لطلب الوحدات.

require.cache

أُضيف مع الإصدار: v0.3.0.

تُخزَّن الوحدات مؤقتًا في هذا الكائن عندما تكون مطلوبة. عند حذف قيمة مفتاحية من هذا الكائن، سيُعيد require التالي تحميل الوحدة. علما بأن هذا لا ينطبق على addons الأصلية، إذ تُنتج إعادة التحميل خطأً.

require.extensions

أضيفت مع الإصدار: v0.3.0، وأُهمِلت مع الإصدار: v0.10.6. مؤشر الاستقرار: 0 - مُهمَل

إرشاد require حول كيفية التعامل مع بعض امتدادات الملفات.

معالجة الملفات بالامتداد ‎.sjs مثل ‎.js:

مهملة في الماضي، استخدمت هذه القائمة لتحميل الوحدات غير JavaScript إلى Node.js بترجمتها حسب الطلب. ومع ذلك، في الممارسة العملية، هناك سبل أفضل للقيام بذلك، مثل تحميل الوحدات عن طريق بعض برامج Node.js الأخرى، أو ترجمتها إلى JavaScript قبل وقتها.

لما كان نظام الوحدات مغلقًا، فربما لم تُلغى هذه الميزة أبدًا. ومع ذلك، فقد يكون بها أخطاء خفية وتعقيدات من الأفضل تركها بلا مساس.

علما بأن عدد عمليات نظام الملفات التي يجب على نظام الوحدات أداءها من أجل حل جملة require(...)‎ إلى اسم ملف يقاس خطيًا مع عدد الملحقات المسجلة.

وبعبارة أخرى، إضافة الملحقات يبطئ من تحميل الوحدة وينبغي تجنبها.

require.main

أُضيفت مع الإصدار: v0.1.17.

كائن الوحدة Module الذي يمثل سكربت الإدخال المُحمَّل عند بدء عملية Node.js. راجع "الوصول إلى الوحدة الرئيسية".

في سكربت entry.js:

console.log(require.main);
node entry.js
Module {
  id: '.',
  exports: {},
  parent: null,
  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])‎

الإصدار التغييرات
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.

hgكائن module

أُضيف مع الإصدار: v0.1.16.

في كل وحدة، يكون متغير module الحر مرجعًا للكائن الذي يمثل الوحدة الحالية. لمزيد من المواءمة، يمكن الوصول إلى module.exports أيضًا عبر وحدة exports العامة. module ليست في الواقع عامة ولكنها بالأحرى محلية لكل وحدة.

module.children

أُضيف مع الإصدار: v0.1.16.

    • من النوع ‎<module[]>‎.

كائنات الوحدة المطلوبة من قِبلها.

module.exports

أُضيف مع الإصدار: v0.1.16.

يُنشِئ نظامُ Module كائنَ module.exports. في بعض الأحيان يكون هذا الأمر غير مقبول؛ يريد الكثير أن تكون وحدتهم مثيلة لصنف ما. للقيام بذلك، يجب تعيين الكائن المطلوب تصديره إلى module.exports. لاحظ أن تعيين الكائن المطلوب إلى exports سيربط ببساطة متغير exports المحلي، وهو على الأرجح ليس هو المطلوب.

على سبيل المثال لنفترض عمل وحدة تسمى a.js:

const EventEmitter = require('events');

module.exports = new EventEmitter();

// القيام ببعض العمل ، وبعد بعض الوقت إطلاق
// الحدث "ready" من الوحدة نفسها.
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);

اختصار الصادرات

أُضيف مع الإصدار: v0.1.16.

متغير 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

أُضيف مع الإصدار: v0.1.16.

اسم الملف المستبين بالكامل للوحدة.

module.id

أُضيف مع الإصدار: v0.1.16.

معرف الوحدة. عادة هو اسم الملف المحلول بالكامل.

module.loaded

أُضيف مع الإصدار: v0.1.16.

ما إذا كان قد انتهى تحميل الوحدة أم لا، أو ما يزال في عملية التحميل.

module.parent

أُضيف مع الإصدار: v0.1.16.

    • من النوع <module>.

الوحدة التي تتطلبها أولاً.

module.paths

أُضيف مع الإصدار: v0.4.0.

مسارات البحث عن الوحدة.

module.require(id)‎

أُضيف مع الإصدار: v0.5.1.

القيمة المُعادة: <Object> من النوع module.exports من الوحدة المستبينة.

يوفر التابع module.require طريقة لتحميل الوحدة كما لو اُستدعِي require()‎ من الوحدة الأصلية.

للقيام بذلك، من الضروري الحصول على مرجع إلى كائن module. لما كان require()‎ يعيد module.exports، وتتوفر module عادة في التعليمات البرمجية فقط لوحدة معينة، فإنه يجب تصديرها صراحة من أجل استخدامها.

كائن Module

أُضيف مع الإصدار: v0.3.7.

يوفر توابع خدمة عامة عند التعامل مع نسخ من الوحدة Module –غالباً ما يُرى متغير module في وحدات الملف. الوصول إليها عن طريق require('module').

module.builtinModules

أُضيف مع الإصدار: v9.3.0.

قائمة بأسماء كافة الوحدات التي توفرها Node.js. يمكن استخدامها للتحقق من إذا كان الوحدة تُصان من قِبَل طرف ثالث أو لا.

لاحظ أن الوحدة module في هذا السياق ليست نفس الكائن الذي توفره وحدة التغليف module wrapper. للوصول إليها، يجب طلب الوحدة Module:

const builtin = require('module').builtinModules;

مصادر