قدمنا في مقال سابق مدخلًا إلى مفاهيم البرمجة كائنية التوجه في جافا سكريبت، وناقشنا مثالًا عن استخدام مبادئها لنمذجة مدرسين وطلاب في مدرسة. كما تحدثنا أيضًا عن إمكانية استخدام الكائنات المجردة prototype والدوال البانية constructor لتنفيذ نماذج مشابهة، والميزات المرتبطة بمفاهيم البرمجة غرضية التوجه التقليدية التي تقدمها جافا سكريبت.
سنتوسع في شرح هذه الميزات في مقالنا، وعليك الانتباه إلى أن الميزات التي نشرحها ليست طريقة جديدة لدمج الكائنات classes، وأن الكائنات ستستخدم دائمًا كائنات prototype خلف الكواليس. وكل ما هنالك أنها وسيلة لتسهيل بناء سلسلة الكائنات prototype.
ننصحك قبل أن تبدأ العمل معنا في هذه السلسلة أن تطلع على:
-
أساسيات جافاسكريبت كما شرحناها في سلسلة المقالات السابقة.
-
أساسيات البرمجة كائنية التوجه في جافا سكربت كما شرحناها في مقال أساسيات العمل مع الكائنات في جافاسكربت، ومقال الوراثة باستخدام الكائنات في جافاسكربت إضافة إلى مفاهيم أساسية في البرمجة كائنية التوجه.
اﻷصناف والدوال البانية
يمكن التصريح عن صنف باستخدام الكلمة المحجوزة new
، إليك كيفية تعريف الصنف Person
الذي تعاملنا معه في مقال سابق:
class Person { name; constructor(name) { this.name = name; } introduceSelf() { console.log(`Hi! I'm ${this.name}`); } }
تُصرّح الشيفرة السابقة صنفًا يُدعى person
له:
-
الخاصية
name
. -
دالة بانية لها معامل وحيد
name
ويُستخدم لتهيئة قيمة الخاصيةname
للكائن الجديد. -
تابع
()introduceSelf
يمكنه اﻹشارة إلى خاصيات الكائن باستخدام التعليمةthis
.
يُعد التصريح ;name
في البداية اختياريًا يمكن الاستغناء عنه، إذ يُنشئ السطر ;this.name = name
في الدالة البانية الخاصية name
قبل تهيئتها. لكن التصريح عمومًا عن الخاصياتن يجعل الشيفرة أسهل قراءة، ويوّضح تمامًا الخاصيات التي يمتلكها الصنف. كما تستطيع أيضًا تهيئة الخاصية بقيمة افتراضية عند التصريح عنها على الشكل ; =name
.
عّرفت الدالة البانية باستخدام الكلمة المحجوزة constructor
، وكما هو الحال عند تعريف الدالة البانية خارج الصنف،سيكون لها المهام التالية:
- إنشاء كائن جديد.
-
ربط التعليمة
this
بالكائن الجديد كي تتمكن من استخدام هذه التعليمة في اﻹشارة إلى الكائن ضمن شيفرتها. - تنفيذ شيفرة الدالة البانية.
- إعادة الكائن الجديد.
وبالعودة إلى شيفرة التصريح عن الكائن السابق، تستطيع إنشاء واستخدام نسخة جديدة من الكائن Person
كالتالي:
const giles = new Person("Giles"); giles.introduceSelf(); // Hi! I'm Giles
لاحظ كيف نستدعي الدالة البانية باستخدام اسم الصنف، وهو Person
في مثالنا.
حذف الدالة البانية
إن لم تكن هناك حاجة لتهيئة قيم الخاصيات، تستطيع إهمال الدالة البانية، وعندها ستُولَّد دالة بانية افتراضية:
class Animal { sleep() { console.log("zzzzzzz"); } } const spot = new Animal(); spot.sleep(); // 'zzzzzzz'
الوراثة Inheritance
تسمح الوراثة بإنشاء علاقة تسلسلية بين الكائنات، حيث يمكن للكائنات الفرعية أو الأبناء sub classes وراثة الخاصيات والتوابع من الكائنات الأساسية أو الآباء base classes، وفي نفس الوقت يمكنها تعديلها أو إضافة خصائص جديدة. لنتعرف على كيفية تحقيق مفهوم الوراثة في جافا سكريبت، لذا دعونا نستخدم الصنفPerson
السابق في تعريف كائن فرعي أو كائن ابن له باسمprofessor
:
class Professor extends Person { teaches; constructor(name, teaches) { super(name); this.teaches = teaches; } introduceSelf() { console.log( `My name is ${this.name}, and I will be your ${this.teaches} professor.`, ); } grade(paper) { const grade = Math.floor(Math.random() * (5 - 1) + 1); console.log(grade); } }
نستخدم الكلمة المحجوزة extends
للدالة إلى أن الصنف الجديد يرث صنفًا آخر. ويضيف الصنف Professor
خاصية جديدة هي teaches
لهذا نُصرّح عنها. وطالما أننا نريد تهيئة قيمة تلك الخاصية عندما ننشئ كائنًا جديدًا من الصنف Professor
، لا بد من تعريف دالة بانية تأخذ معاملين هما name
و teaches
. وما تفعله الدالة البانية هنا، هو استدعاء الدالة البانية للصنف اﻷب باستخدام التابع ()super
ممررة له المعامل name
وستتكفل الدالة البانية للصنف اﻷب بضبط قيمة الخاصية name
. وبعدها تهيئ الدالة البانية للصنف Professor
قيمة الخاصية teaches
.
ملاحظة: إن كان على الدالة البانية للصنف الابن تهيئة أية قيم خاصة به، عليها أولًا التأكد من تهيئة قيم الصنف الأب لهذا الصنف من خلال استدعاء الدالة ()super
وتمرير قيم أية معاملات تحتاجها.
كما يتجاوز الكائن الابن التابع ()introduceSelf
الخاص بالصنف اﻷب ويقدّم نسخته الخاصة، ويضيف التابع ()grade
لتصحيح اﻷوراق (طبعًا في مثالنا يوزّع المدرّس علامات عشوائية على الأوراق). وهكذا سنتمكن اﻵن من إنشاء مدرسين جديد:
const walsh = new Professor("Walsh", "Psychology"); walsh.introduceSelf(); // 'My name is Walsh, and I will be your Psychology professor' walsh.grade("my paper"); // some random grade
التغليف Encapsulation
لنرى أخيرًا كيف ننجز مفهوم التغليف في جافاسكريبت. فقد ناقشنا في مقال سابق كيف أردنا أن تكون الخاصية year
للكائن Student
خاصّة، كي نتمكن من تغيير شروط التسجيل في دروس الرماية دون اﻹخلال بالشيفرة التي تستخدم الكائن Student
.
class Student extends Person { #year; constructor(name, year) { super(name); this.#year = year; } introduceSelf() { console.log(`Hi! I'm ${this.name}, and I'm in year ${this.#year}.`); } canStudyArchery() { return this.#year > 1; } }
وما فعلناه في التصريح السابق عن الصنف، أننا جعلنا الخاصية year#
خاصّة بالصنف، وهكذا سنتمكن من بناء كائن Student
يستخدم الخاصية year#
داخليًا، وسيعطي المتصفح رسالة خطأ إن حاولت شيفرة خارج الكائن الوصول إليها.
const summers = new Student("Summers", 2); summers.introduceSelf(); // Hi! I'm Summers, and I'm in year 2. summers.canStudyArchery(); // true summers.#year; // SyntaxError
ملاحظة: يمكن للشيفرة المكتوبة ضمن طرفية جافا سكريبت في المتصفح الوصول إلى الخاصيات الخاصة حتى لو كانت خارج الصنف. وهذا أمر خاص لتحرير أدوات مطوري ويب فقط من قيود الصياغة اللغوية لجافا سكريبت.
إذًا ينبغي أن يبدأ اسم الخاصيات الخاصة بالمحرف #
ويجب أن يُصرّح عنها داخل الصنف.
التوابع الخاصة Private methods
بإمكانك تحديد توابع أيضًا لتكون توابع خاصة بالصنف كما في الخاصيات، بحيث تكون تابعة للكائن نفسه ولا يمكن الوصول إليها من خارجه الدالة خاصة بالكائن نفسه، كما في الخاصيات ولا بد في هذه الحالة أن يبدأ اسم التابع بالمحرف #
أيضًا، وعندها ستُستدعى فقط من قبل توابع هذا الصنف كما في المثال التالي:
class Example { somePublicMethod() { this.#somePrivateMethod(); } #somePrivateMethod() { console.log("You called me?"); } } const myExample = new Example(); myExample.somePublicMethod(); // 'You called me?' myExample.#somePrivateMethod(); // SyntaxError
الخلاصة
ناقشنا في هذا المقال الأدوات التي تقدمها جافا سكريبت لكتابة الكائنات والتعامل معها في البرامج كائنية التوجه، وتجدر الإشارة إلى أننا لم نغطي كل النقاط المتعلقة بالموضوع في هذا المقال، لكن ما قدمناه سيكون كافيًا في البداية.
ترجمة -وبتصرف- لمقال Classes in JavaScript
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.