البرمجة

فهم الوراثة النموذجية في جافاسكربت

الوراثة النموذجية (Prototypal Inheritance) في جافاسكربت: الجزء الثاني

الوراثة النموذجية (Prototypal Inheritance) هي إحدى الخصائص المهمة في جافاسكربت التي تميزها عن غيرها من لغات البرمجة التي تعتمد على الوراثة الكائنية (Class-based inheritance). في الجزء الأول من هذا المقال، تم التعرف على الأساسيات والمفاهيم الأولية للوراثة النموذجية في جافاسكربت. الآن، سنتناول تفاصيل أعمق حول كيفية عمل الوراثة النموذجية في جافاسكربت وكيف يمكن الاستفادة منها لبناء تطبيقات أكثر فعالية وقوة.

1. مفهوم الـ Prototype Chain

يعتبر “السلسلة النموذجية” أو الـ “Prototype Chain” من الركائز الأساسية لفهم الوراثة النموذجية في جافاسكربت. في جافاسكربت، كل كائن (Object) يحتوي على خاصية تسمى prototype تشير إلى كائن آخر، وبدوره هذا الكائن يحتوي على خاصية prototype كذلك، وهكذا تتكون سلسلة من الكائنات. عند محاولة الوصول إلى خاصية معينة في كائن ما، إذا كانت هذه الخاصية غير موجودة في الكائن نفسه، سيبحث جافاسكربت في الكائن الموجود في الـ prototype، وإذا لم يجدها، ينتقل إلى الـ prototype الخاص بالكائن الذي يشير إليه الكائن الآخر، وهكذا يستمر البحث حتى الوصول إلى الكائن الأساسي (Object.prototype) الذي يمثل الجذر النهائي للسلسلة.

لنوضح ذلك بمثال:

javascript
let animal = { eat: function() { console.log("Eating..."); } }; let dog = Object.create(animal); dog.bark = function() { console.log("Barking..."); }; dog.bark(); // Barking... dog.eat(); // Eating...

في هذا المثال، dog ليس لديه خاصية eat بشكل مباشر، لكنه يستطيع الوصول إليها عبر السلسلة النموذجية من خلال كائن الـ animal. هذا هو مبدأ الوراثة النموذجية في جافاسكربت: إذا لم يكن الكائن يحتوي على الخاصية المطلوبة، فإنه يبحث عنها في الـ prototype.

2. كيفية إنشاء كائنات باستخدام الوراثة النموذجية

في جافاسكربت، يمكننا إنشاء كائنات تستخدم الوراثة النموذجية بطرق متعددة. الطريقة الأكثر شيوعًا هي استخدام Object.create() لإنشاء كائن جديد استنادًا إلى كائن آخر. سنناقش في هذا الجزء بعض الأساليب المختلفة لإنشاء كائنات تعتمد على الوراثة النموذجية.

2.1. استخدام Object.create()

كما سبق وذكرنا، تُعتبر Object.create() إحدى الطرق لإنشاء كائن جديد مع تعيين كائن آخر كـ prototype لهذا الكائن. تُستخدم هذه الطريقة بشكل شائع في جافاسكربت لإنشاء الكائنات بطريقة أكثر مرونة.

javascript
let person = { greet: function() { console.log("Hello!"); } }; let student = Object.create(person); student.study = function() { console.log("Studying..."); }; student.greet(); // Hello! student.study(); // Studying...

في هذا المثال، student يرث الوظيفة greet() من الكائن person، ولكنه يحتوي أيضًا على خاصية study() الخاصة به.

2.2. استخدام الدوال الإنشائية (Constructor Functions)

الدوال الإنشائية هي طريقة أخرى لإنشاء كائنات في جافاسكربت باستخدام الوراثة النموذجية. في هذه الحالة، يتم استخدام الدوال لإنشاء كائنات جديدة حيث يصبح prototype الخاص بالكائن هو الكائن الذي تم تعيينه بواسطة الدالة الإنشائية.

javascript
function Animal(name) { this.name = name; } Animal.prototype.sayHello = function() { console.log("Hello, my name is " + this.name); }; let dog = new Animal("Buddy"); dog.sayHello(); // Hello, my name is Buddy

هنا، تم استخدام دالة Animal لإنشاء كائن جديد. هذه الدالة تعد بمثابة “فئة” في جافاسكربت، وتستخدم prototype لتعريف السلوكيات المشتركة بين الكائنات المشتقة.

2.3. استخدام الكائنات في دوال الإنشاء الحديثة (ES6 Classes)

مع إدخال الدوال الإنشائية في ES6، أصبح من الممكن استخدام الكائنات بشكل أكثر تنظيمًا من خلال الـ class. على الرغم من أن الكائنات التي تم إنشاؤها باستخدام class تعتمد على الوراثة النموذجية، إلا أنها تُعتبر طريقة أكثر وضوحًا لتنظيم الكود.

javascript
class Animal { constructor(name) { this.name = name; } sayHello() { console.log("Hello, my name is " + this.name); } } class Dog extends Animal { bark() { console.log("Woof!"); } } let myDog = new Dog("Buddy"); myDog.sayHello(); // Hello, my name is Buddy myDog.bark(); // Woof!

في هذا المثال، تم استخدام class لإنشاء كائنات مع دعم الوراثة النموذجية من خلال extends. هنا، Dog يرث كل وظائف Animal ويمكنه إضافة وظائف خاصة به.

3. الفرق بين الوراثة النموذجية والوراثة الكائنية

الوراثة النموذجية تختلف عن الوراثة الكائنية التي تعتمد على الفئات (Classes) في عدة جوانب. في الوراثة النموذجية، لا يتم تحديد “الفئات” (Classes) بشكل صريح، بل يتم مشاركة الخصائص والسلوكيات بين الكائنات من خلال الـ prototype. بينما في الوراثة الكائنية، يتم تعريف فئة (Class) تحتوي على خصائص وسلوكيات معينة، ومن ثم يتم إنشاء كائنات من تلك الفئة.

واحدة من الفروقات الرئيسية هي أن الوراثة النموذجية في جافاسكربت توفر درجة عالية من المرونة، حيث يمكن إضافة أو تعديل خصائص الـ prototype في وقت لاحق، بينما في الوراثة الكائنية، تتبع الكائنات هيكلًا أكثر صرامة.

4. مزايا وعيوب الوراثة النموذجية

4.1. المزايا

  • المرونة: الوراثة النموذجية تتيح لك تعديل خصائص prototype في أي وقت بعد إنشاء الكائنات. هذا يتيح لك إضافة سلوكيات جديدة أو تغيير السلوكيات الموجودة دون الحاجة إلى تعديل الكود الأصلي.

  • الكفاءة: بما أن الكائنات تشارك الـ prototype، فإن هذا يقلل من استهلاك الذاكرة ويزيد من كفاءة البرنامج. لا يتم تكرار نفس الوظائف والخصائص في كل كائن، بل تتم مشاركتها بين الكائنات من خلال السلسلة النموذجية.

  • سهولة التوسع: يمكن إضافة أو تعديل خصائص الكائنات في وقت لاحق دون الحاجة لإعادة تعريف الكائنات من البداية.

4.2. العيوب

  • التعقيد: في بعض الحالات، قد يؤدي استخدام الوراثة النموذجية إلى بعض التعقيد في تتبع الكود وفهمه، خاصة عندما تصبح السلسلة النموذجية طويلة جدًا.

  • القيود: على الرغم من أن الوراثة النموذجية توفر مرونة، إلا أن جافاسكربت لا يحتوي على نظام وراثة كائني كامل مثل لغات أخرى كـ C++ أو Java، مما قد يجعل بعض الأنماط البرمجية معقدة أكثر.

5. مفاهيم متقدمة: الـ Prototype Chain و Inheritance Hierarchies

من المفيد أيضًا أن نفهم كيفية بناء تسلسل هرمي للوراثة (Inheritance Hierarchy) باستخدام الـ prototype. عندما نستخدم دوال إنشاء مع وراثة نموذجية، يمكننا بناء هيكل هرمي معقد من الكائنات.

javascript
function Animal(name) { this.name = name; } Animal.prototype.sayHello = function() { console.log("Hello, my name is " + this.name); }; function Dog(name) { Animal.call(this, name); } Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog; Dog.prototype.bark = function() { console.log("Woof!"); }; let myDog = new Dog("Rex"); myDog.sayHello(); // Hello, my name is Rex myDog.bark(); // Woof!

في هذا المثال، قمنا بإنشاء هيكل هرمي حيث يرث الكائن Dog من Animal. باستخدام Object.create(), تم تعيين Dog.prototype إلى Animal.prototype، مما يتيح لك استدعاء الأساليب الموروثة من Animal في كائنات Dog.

6. الخلاصة

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