Menu
العربية

الكلاسات في JavaScript: class و constructor و new

كيف تعمل الكلاسات في JavaScript فعلياً: الـ constructor والميثودز وحقول الـ instance والـ getters و setters، مع الفكرة الحقيقية وراء كلمة class.

الكلاس قالب لإنشاء الكائنات

الكلاس في javascript هو قالب يصف شكل وسلوك نوع معيّن من الكائنات. تعرّف القالب مرة واحدة، ثم تنشئ منه ما تحتاجه من النسخ (instances). كل نسخة تحتفظ ببياناتها الخاصة، لكنها تشترك جميعًا في نفس الدوال (methods).

الصيغة الأساسية تبدو هكذا:

index.js
Output
Click Run to see the output here.

class User { ... } يُمثّل المخطط أو القالب، بينما new User(...) هو ما يُنشئ نسخة فعلية (instance). كل نسخة تحصل على name وemail خاصّين بها، لكنهما تتشاركان نفس الدالة greet المُعرَّفة على الكلاس.

نسختان، مجموعتان من البيانات، ومجموعة واحدة من الدوال. هذه هي الفكرة باختصار.

دور الـ constructor في تهيئة كل نسخة

الـ constructor دالة خاصة تعمل مرة واحدة فقط لكل نسخة، تحديدًا في اللحظة التي تُنشئها فيها الكلمة new. مهمتها تهيئة الكائن الجديد، وعادةً ما يتم ذلك بنسخ الوسائط (arguments) إلى this:

index.js
Output
Click Run to see the output here.

this داخل الـ constructor يشير إلى النسخة الجديدة (instance) اللي لسّه بتتبني. لما تكتب this.x = x، فأنت بتحط x على هذا الكائن بالذات. كل استدعاء لـ new Point(...) يحصل على this خاص به، وبالتالي x و y خاصين به.

لو ما كتبتش constructor بنفسك، JavaScript بيديك واحد فاضي تلقائيًا. يعني مش لازم تكتب واحد إلا لما يكون عندك فعلاً إعدادات تحتاج تنفّذها.

الكلمة المفتاحية new هي السر وراء عمل الكلاسات

استدعاء أي class بدون new بيرمي خطأ:

const p = Point(3, 4);
// TypeError: لا يمكن استدعاء مُنشئ الصنف Point بدون 'new'

هذا سلوك مقصود. الكلمة new تقوم بأربع مهام بالترتيب:

  1. تنشئ كائنًا فارغًا جديدًا.
  2. تربطه بالـ prototype الخاص بالكلاس (حيث توجد التوابع).
  3. تُشغّل الـ constructor مع ربط this بالكائن الجديد.
  4. تُعيد الكائن الجديد.

بدون new، لا يحدث أي من ذلك. الكلاس يفرض هذه القاعدة حتى لا تنساها عن طريق الخطأ — وهذا تحسّن حقيقي مقارنةً بما قبل ظهور class، حين كان نسيان new يُفسد الكائن العام (global object) بصمت.

التوابع مشتركة، أما الحقول فخاصة بكل نسخة (instance في javascript)

التوابع المُعرّفة داخل جسم الكلاس تعيش على الـ prototype — نسخة واحدة يتشاركها كل الكائنات. أما حقول النسخة (أي ما تُسنده إلى this) فهي مستقلة، ولكل كائن نسخته الخاصة منها.

index.js
Output
Click Run to see the output here.

كلا الكائنين (instance) يملك count خاصًا به، فزيادة أحدهما لا تؤثر على الآخر. لكن a.increment و b.increment هما في الحقيقة نفس الدالة تمامًا — مخزّنة مرة واحدة على الـ prototype، ويتم الرجوع إليها عند استدعائها على أي instance. ولهذا السبب فإن الكلاسات في JavaScript موفّرة للذاكرة عند التوسع: ألف instance لا تعني ألف نسخة من كل ميثود.

حقول الكلاس (Class Fields)

يمكنك تعريف الحقول الخاصة بالـ instance مباشرة في بداية جسم الكلاس، خارج الـ constructor:

index.js
Output
Click Run to see the output here.

تُنفَّذ تعريفات الحقول (field declarations) قبل جسم الـ constructor، وكأنها مكتوبة في بدايته. وتكون مفيدة عندما لا تعتمد القيمة الابتدائية على وسائط الـ constructor — فهي أنظف من حشو الـ constructor بأسطر مثل this.count = 0; this.step = 1;.

أما إذا كانت القيم تعتمد على الوسائط، فاترك الإسناد داخل الـ constructor حيث تكون الوسائط في نطاق الوصول.

الـ Getters والـ Setters في javascript

يبدو الـ getter أو الـ setter كأنه دالة (method)، لكنه يتصرف كأنه وصول إلى خاصية عادية. فأنت تقرأ القيمة أو تُسندها دون استخدام الأقواس، بينما تعمل الدالة خلف الكواليس:

index.js
Output
Click Run to see the output here.

لاحظ كيف نستدعي الخصائص: t.fahrenheit (بدون أقواس) تُفعِّل الـ getter، بينما t.fahrenheit = 100 تُفعِّل الـ setter. من الخارج، تبدو fahrenheit وكأنها خاصية عادية، لكنها في الحقيقة تُحسب لحظياً اعتماداً على celsius.

الـ getters مفيدة جداً للقيم المُشتقّة، والـ setters ممتازة للتحقق من القيمة أو تطبيعها وقت الإسناد. لكن لا تُفرط في استخدامها — فإذا كان الـ getter يُنفّذ عمليات ثقيلة، سيُفاجأ من يقرأ الكود بأن ما يبدو مجرد "قراءة خاصية" يُخفي خلفه عملاً حقيقياً.

التابع المختصر، الأسماء المحسوبة، و this

عند تعريف التوابع (methods) داخل الـ class، نستخدم الصياغة المختصرة — بدون كلمة function، وبدون : بين الاسم والجسم:

index.js
Output
Click Run to see the output here.

إرجاع this من add يفتح لك الباب لاستخدام method chaining — كل استدعاء يعيد لك نفس الـ instance، فتقدر تنادي الدالة اللي بعدها مباشرة عليه.

لكن فيه نقطة مهمة لازم تنتبه لها: التوابع (methods) في الكلاسات في javascript ما ترتبط تلقائيًا بالـ instance. لو أخذت التابع لحاله وناديته منفصلًا، راح تفقد قيمة this:

index.js
Output
Click Run to see the output here.

استدعاء g.hello() يشتغل تمام لأن النقطة . هي اللي بتمرر this. أما لو استدعيت fn() لوحدها فالموضوع مش هيظبط. لو محتاج دالة منفصلة ومربوطة مسبقاً بالكائن (وده موقف شائع جداً في معالجات الأحداث)، اربطها جوّه الـ constructor أو استخدم حقل كلاس على شكل arrow function بالشكل ده: hello = () => \Hi, ${this.name}`;`.

مثال عملي مصغّر على class في JavaScript

تعالَ نجمع كل اللي فات مع بعضه — الحقول، الـ constructor، الميثودز، و getter:

index.js
Output
Click Run to see the output here.

سهل القراءة من أول نظرة: الكلاس يوضّح بدقة ما هو BankAccount وما الذي يستطيع فعله.

الكلاسات في جافا سكريبت ما هي إلا دوال

شيء مهم لازم يرسخ في ذهنك: كلمة class ليست سوى سكّر نحوي (syntactic sugar). فتحت الغطاء، الكلاس في جافا سكريبت هو في الحقيقة دالة — وتحديداً دالة بانية (constructor function) — بينما توابعه (methods) تعيش على ClassName.prototype:

index.js
Output
Click Run to see the output here.

typeof User يساوي "function"، والدالة greet موجودة على User.prototype. الكائنات (instances) تصل إلى greet عبر سلسلة البروتوتايب (prototype chain) — وهي نفس الآلية الموجودة في اللغة منذ بدايتها. الكلاسات في JavaScript ما هي إلا صياغة (syntax) أنيقة لتجهيز كل هذا نيابةً عنك.

هذا التصور الذهني سيخدمك لاحقًا كثيرًا: الوراثة، و instanceof، وتتبّع أخطاء سلسلة البروتوتايب، كلها تصبح أوضح بكثير حين تتذكّر أن الـ class في JavaScript ما هو إلا دالة (function) وتوابعها (methods) معلّقة على البروتوتايب الخاص بها.

الخطوة التالية: الوراثة

حتى الآن تعاملنا مع كل class على حدة. لكن في المشاريع الحقيقية غالبًا ما نبني هرميات من الكلاسات — مثلاً Dog نوع من Animal، و AdminUser نوع من User. الكلمتان extends و super هما المسؤولتان عن هذا الأمر، وهذا ما سنتناوله بعد قليل.

الأسئلة الشائعة

كيف ننشئ كلاس في JavaScript؟

استخدم الكلمة المفتاحية class متبوعة باسم الكلاس، ثم افتح بلوك يحتوي على ميثود constructor وبقية الميثودز. لإنشاء نسخة (instance) استدعِ new ClassName(...). مثال: class User { constructor(name) { this.name = name; } } ثم new User('Ada').

ما وظيفة الـ constructor في كلاس JavaScript؟

يتم تنفيذ الـ constructor مرة واحدة عند استدعاء new ClassName(...)، ومهمته تجهيز الـ instance الجديد — عادةً بإسناد القيم الممررة إلى this كخصائص. وإذا لم تكتب واحداً بنفسك، فإن JavaScript يضيف لك constructor افتراضياً فارغاً.

ما الفرق بين class و function في JavaScript؟

تحت الغطاء، الكلاس ما هو إلا دالة (function) — تحديداً constructor function مع ميثودز مرتبطة بالـ prototype. كلمة class في معظمها مجرد syntactic sugar، لكنها تفرض استدعاءها بـ new، وتدعم extends بشكل نظيف، وتجعل الميثودز غير قابلة للتعداد (non-enumerable). في الكود الجديد، يُفضَّل استخدام class بدلاً من كتابة constructor functions يدوياً.

تعلّم البرمجة مع Coddy

ابدأ الآن