الوراثة النموذجية (Prototypal Inheritance) في جافاسكربت: الجزء الأول
الوراثة هي أحد المفاهيم الأساسية في علم البرمجة، وبالأخص في لغات البرمجة التي تدعم الأنماط الكائنية (Object-Oriented Programming)، مثل جافاسكربت. لكن وراثة الكائنات في جافاسكربت تختلف عن الوراثة التقليدية في لغات البرمجة الأخرى مثل جافا أو سي++. في هذه اللغات التقليدية، يتم استخدام الوراثة القائمة على الفئات (Class-Based Inheritance)، حيث يتم تعريف الفئة (Class) التي تحتوي على خصائص وأساليب (Methods)، ومن ثم يمكن للكائنات أن ترث منها.
ولكن جافاسكربت تعتمد على مفهوم مختلف وهو “الوراثة النموذجية” أو ما يعرف بـ “Prototypal Inheritance”. هذا النظام يتيح للكائنات أن ترث مباشرة من كائنات أخرى، مما يتيح مرونة أكبر في كيفية مشاركة الخصائص والأساليب بين الكائنات. لفهم هذا المفهوم بشكل كامل، من الضروري أن نغوص في تفاصيل آلية عمل هذه الوراثة وأثرها على البرمجة في جافاسكربت.
المفهوم الأساسي للوراثة النموذجية في جافاسكربت
في جافاسكربت، كل كائن يحتوي على خاصية تُسمى prototype. هذه الخاصية هي عبارة عن كائن آخر يحتوي على مجموعة من الخصائص والأساليب التي يمكن للكائن الحالي الوصول إليها واستخدامها. يمكن أن نفكر في الـ prototype كـ “نموذج” (prototype) يتم استخدامه لتوريث الخصائص والأساليب من كائن آخر.
عندما نقوم بإنشاء كائن جديد في جافاسكربت، فإن هذا الكائن سيتم ربطه مباشرةً بكائن آخر يُسمى prototype الخاص بالكائن الذي تم إنشاؤه. وبعبارة أخرى، كل كائن في جافاسكربت يحتوي على سلسلة من الكائنات المرتبطة من خلال الـ prototype والتي تُعرف بـ “سلسلة النموذج” أو prototype chain.
كيفية استخدام الوراثة النموذجية
لنقم بشرح كيف يمكن استخدام الوراثة النموذجية في جافاسكربت من خلال مثال عملي. إذا أردنا إنشاء كائن يحتوي على أساليب وخصائص مشتركة بين مجموعة من الكائنات، فإننا نستخدم الـ prototype لإضافة هذه الأساليب.
إنشاء كائن ونموذج باستخدام الوراثة النموذجية
javascriptfunction Person(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.greet = function() {
console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);
};
const person1 = new Person('Alice', 30);
const person2 = new Person('Bob', 25);
person1.greet(); // Output: Hello, my name is Alice and I am 30 years old.
person2.greet(); // Output: Hello, my name is Bob and I am 25 years old.
في المثال أعلاه، قمنا بإنشاء دالة منشئ (Person) والتي تُستخدم لإنشاء كائنات جديدة تحتوي على خصائص name و age. ثم أضفنا دالة greet إلى prototype الخاص بـ Person. هذا يعني أن كل كائن يتم إنشاؤه من Person سيحصل على الوصول إلى هذه الدالة عبر سلسلة الـ prototype.
سلسلة النموذج (Prototype Chain)
في جافاسكربت، عندما يتم طلب خاصية أو طريقة من كائن، فإن المحرك يبدأ بالبحث عن هذه الخاصية في الكائن نفسه. إذا لم يتم العثور عليها، يتم البحث في الكائن الذي يحتوي على الـ prototype لهذا الكائن. إذا لم يتم العثور على الخاصية في الـ prototype، ينتقل البحث إلى الـ prototype الخاص بـ prototype وهكذا. هذه هي “سلسلة النموذج” أو prototype chain.
لنأخذ مثالًا آخر لفهم كيفية عمل سلسلة النموذج بشكل أعمق:
javascriptfunction Animal(name) {
this.name = name;
}
Animal.prototype.sayHello = function() {
console.log(`Hello, I am a(n) ${this.name}`);
};
function Dog(name) {
Animal.call(this, name); // Call the Animal constructor
}
Dog.prototype = Object.create(Animal.prototype); // Set the prototype of Dog to Animal's prototype
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log('Woof! Woof!');
};
const dog = new Dog('dog');
dog.sayHello(); // Output: Hello, I am a(n) dog
dog.bark(); // Output: Woof! Woof!
في هذا المثال، قمنا بإنشاء كائن Dog الذي يرث من Animal. لاحظ أننا استخدمنا Object.create(Animal.prototype) لربط Dog.prototype بـ Animal.prototype. وبذلك، حصل كائن dog على الوصول إلى الأساليب الموجودة في Animal.prototype مثل sayHello()، بالإضافة إلى الأساليب الخاصة بـ Dog مثل bark().
الوراثة في الكائنات المدمجة
يمكن للوراثة النموذجية في جافاسكربت أن تحدث بطرق معقدة، بحيث يمكن ربط كائنات متعددة معًا عبر سلسلة من الـ prototypes. يمكن للـ prototype أن يكون كائنًا يحتوي على خصائص وأساليب خاصة به، ويمكن لهذه الأساليب أن تكون موروثة من كائنات أخرى عبر سلسلة من النماذج.
مثال على التوريث المتعدد
javascriptfunction Animal(name) {
this.name = name;
}
Animal.prototype.sayHello = function() {
console.log(`Hello, I am an animal named ${this.name}`);
};
function Bird(name) {
Animal.call(this, name);
}
Bird.prototype = Object.create(Animal.prototype);
Bird.prototype.constructor = Bird;
Bird.prototype.fly = function() {
console.log(`${this.name} is flying!`);
};
const parrot = new Bird('Parrot');
parrot.sayHello(); // Output: Hello, I am an animal named Parrot
parrot.fly(); // Output: Parrot is flying!
في هذا المثال، قمنا بإنشاء كائن Bird الذي يرث من Animal ويضيف خاصية جديدة هي fly. وبالتالي، يمكن لكائن parrot أن يستخدم الأساليب الموروثة من Animal مثل sayHello بالإضافة إلى الأسلوب الجديد fly.
إيجابيات الوراثة النموذجية
-
المرونة في الوراثة: الوراثة النموذجية في جافاسكربت توفر مرونة أكبر من الوراثة التقليدية (الفئات)، حيث يمكن أن يتم الربط بين الكائنات بسهولة عن طريق الـ
prototype، ويمكن لكل كائن أن يرث من كائن آخر بشكل ديناميكي. -
تقليل التكرار: يمكن لمجموعة من الكائنات أن تشارك نفس الأساليب عن طريق ربطها بـ
prototypeبدلاً من تكرار الأساليب في كل كائن على حدة، مما يحسن من كفاءة البرنامج. -
تحسين الأداء: من خلال ربط الكائنات بـ
prototype، يمكن للبرنامج أن يعمل بشكل أكثر كفاءة من حيث استخدام الذاكرة، حيث يتم حفظ الأساليب المشتركة في مكان واحد.
التحديات في الوراثة النموذجية
رغم المزايا العديدة التي توفرها الوراثة النموذجية، إلا أن هناك بعض التحديات التي قد يواجهها المبرمجون عند استخدامها. من بين هذه التحديات:
-
الصعوبة في تتبع الوراثة: مع تعدد الطبقات في سلسلة النموذج، قد يصبح من الصعب تتبع كيفية توريث الأساليب والخصائص عبر الكائنات المختلفة.
-
التشابك بين الكائنات: قد يؤدي الربط المفرط بين الكائنات عبر الـ
prototypeإلى حدوث تعقيدات في البرنامج، حيث يصبح من الصعب فصل أو تعديل العلاقات بين الكائنات.
خاتمة الجزء الأول
الوراثة النموذجية هي أحد المفاهيم الأساسية التي تميز جافاسكربت عن غيرها من لغات البرمجة. من خلال استخدام الـ prototype، يمكن للكائنات أن ترث الخصائص والأساليب بشكل مرن وديناميكي، مما يتيح مرونة كبيرة في تطوير البرمجيات. في الجزء الثاني من هذا المقال، سنستعرض المزيد من التفاصيل حول كيفية التعامل مع الوراثة النموذجية في جافاسكربت، بما في ذلك تحسينات الأداء وكيفية تجنب بعض المشكلات الشائعة.

