Programming Basics SQL HTML CSS JavaScript Python C++ Java JavaFX Swing Problem Solving English English Conversations Computer Fundamentals Learn Typing

جافاسكربتالوراثة

  • مفهوم الوراثة
  • الكلمة المفتاحية extends
  • أمثلة على أشكال الوراثة
  • الكلمة المفتاحية super

مفهوم الوراثة

إبتداءاًَ من الإصدار ES6 أصبحت جافاسكربت تدعم الوراثة ( Inheritance ) و التي تعني أن الكلاس يمكنه أن يرث من كلاس آخر و بالتالي يحصل على جميع الدوال و المتغيرات الموجودة فيه.

الوراثة يمكن الإستفادة منها للحد من تكرار الكود، فمثلاً إذا كنت تريد إنشاء كلاس جديد و كان لديك كلاس جاهز يحتوي على متغيرات و دوال تحتاج أن تكتبها فيه، فيمكنك جعل الكلاس يرثها كما هي بدلاً من إعادة تعريفها فيه، و بعدها يمكنك إستخدام جميع المتغيرات و الدوال التي ورثها الكلاس الجديد من الكلاس الجاهز.


مفهوم الكلاس الأب و الكلاس الإبن

  • الكلاس الذي يرث من كلاس آخر يعتبر بمثابة إبن له لذلك يقال له Subclass أو Derived class أو Extended class أو Child class.
  • الكلاس الذي يوّرث محتوياته لكلاس آخر يعتبر بمثابة أب له لذلك فيقال له Superclass أو Base class أو Parent class.

الكلاس الإبن يرث كل المتغيرات، الخصائص و الدوال العامة الموجودة في الكلاس الأب ما عدا الكونستركتور و الدوال الثابتة الموجودة فيه.

الكلمة المفتاحية extends

لجعل الكلاس يرث من كلاس آخر نستخدم الكلمة المفتاحية extends على النحو التالي.

class childClass extends parentClass {
    // Child class body
}
  • childClass — يقصد بها إسم الكلاس الإبن.
  • parentClass — يقصد بها إسم الكلاس الأب.
  • Child class body — يقصد بها الأشياء الخاصة بالكلاس الإبن إضافةً إلى الأشياء التي ورثها من الكلاس الأب.

أمثلة على أشكال الوراثة

في جافاسكربت يوجد 3 أشكال للوراثة كما هو موضح في الجدول التالي.

أنواع الوراثة في جافاسكربت - Javascript Inheritance types


إذاَ أشكال الوراثة في جافاسكربت هي كالتالي:

  • وراثة فردية — تعني كلاس يرث من كلاس واحد فقط.
  • وراثة متتالية — تعني كلاس يرث من كلاس و الذي بدوره يرث من كلاس آخر.
  • وراثة هرمية — تعني أن الكلاس موروث من قبل أكثر من كلاس.

في جافاسكربت، الكلاس يمكنه أن يرث بشكل مباشر من كلاس واحد فقط.


مثال عن الوراثة الفردية

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على متغير إسمه x و دالة إسمها printMessage().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على متغير إسمه y و يرث من الكلاس A.

الوراثة الفردية في جافاسكربت - Single inheritance in javascript

مثال

// printMessage و دالة إسمها x يحتوي على متغير إسمه A هنا قمنا بتعريف كلاس إسمه
class A {
x = 10;
printMessage() {
document.write('Hello from class A <br>');
}
}
// y و يحتوي على متغير إسمه A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
class B extends A {
y = 20;
}
// b إسمه B هنا قمنا بإنشاء كائن من الكلاس
let b = new B();
// B و الذي تم تعريفه في الكلاس b الموجود في الكائن y هنا قمنا بطباعة قيمة المتغير
document.write('b.y = ' + b.y + '<br>');
// A من الكلاس B و الذي ورثه الكلاس b الموجود في الكائن x هنا قمنا بطباعة قيمة المتغير
document.write('b.x = ' + b.x + '<br>');
// A من الكلاس B و التي ورثها الكلاس b الموجودة في الكائن printMessage() هنا قمنا باستدعاء الدالة
b.printMessage();
// printMessage و دالة إسمها x يحتوي على متغير إسمه A هنا قمنا بتعريف كلاس إسمه class A { x = 10; printMessage() { document.write('Hello from class A <br>'); } } // y و يحتوي على متغير إسمه A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه class B extends A { y = 20; } // b إسمه B هنا قمنا بإنشاء كائن من الكلاس let b = new B(); // B و الذي تم تعريفه في الكلاس b الموجود في الكائن y هنا قمنا بطباعة قيمة المتغير document.write('b.y = ' + b.y + '<br>'); // A من الكلاس B و الذي ورثه الكلاس b الموجود في الكائن x هنا قمنا بطباعة قيمة المتغير document.write('b.x = ' + b.x + '<br>'); // A من الكلاس B و التي ورثها الكلاس b الموجودة في الكائن printMessage() هنا قمنا باستدعاء الدالة b.printMessage();

نتيجة التشغيل:

y = 20
x = 10
Hello from class A
جرب الكود

مثال عن الوراثة المتتالية

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها printA().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها printB() و يرث من الكلاس A.
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها printC() و يرث من الكلاس B.

الوراثة المتتالية في جافاسكربت - Multi-level inheritance in javascript

مثال

// printA يحتوي على دالة إسمهاA هنا قمنا بتعريف كلاس إسمه
class A {
printA() {
document.write('Hello from class A <br>');
}
}
// printB و يحتوي على دالة إسمها A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
class B extends A {
printB() {
document.write('Hello from class B <br>');
}
}
// printC و يحتوي على دالة إسمها B يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
class C extends B {
printC() {
document.write('Hello from class C <br>');
}
}
// C إسمه C هنا قمنا بإنشاء كائن من الكلاس
let c = new C();
// c هنا قمنا باستدعاء جميع الدوال الموجودة في الكائن
c.printA();
c.printB();
c.printC();
// printA يحتوي على دالة إسمهاA هنا قمنا بتعريف كلاس إسمه class A { printA() { document.write('Hello from class A <br>'); } } // printB و يحتوي على دالة إسمها A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه class B extends A { printB() { document.write('Hello from class B <br>'); } } // printC و يحتوي على دالة إسمها B يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه class C extends B { printC() { document.write('Hello from class C <br>'); } } // C إسمه C هنا قمنا بإنشاء كائن من الكلاس let c = new C(); // c هنا قمنا باستدعاء جميع الدوال الموجودة في الكائن c.printA(); c.printB(); c.printC();

نتيجة التشغيل:

Hello from class A
Hello from class B
Hello from class C
جرب الكود

مثال عن الوراثة الهرمية

في المثال التالي قمنا بتعريف كلاس إسمه A يحتوي على دالة إسمها printA().
بعدها قمنا بإنشاء كلاس إسمه B يحتوي على دالة إسمها printB() و يرث من الكلاس A.
بعدها قمنا بإنشاء كلاس إسمه C يحتوي على دالة إسمها printC() و يرث من الكلاس A أيضاً.

الوراثة الهرمية في جافاسكربت - Hierarchical inheritance in javascript

مثال

// printA يحتوي على دالة إسمهاA هنا قمنا بتعريف كلاس إسمه
class A {
printA() {
document.write('Hello from class A <br>');
}
}
// printB و يحتوي على دالة إسمها A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
class B extends A {
printB() {
document.write('Hello from class B <br>');
}
}
// printC و يحتوي على دالة إسمها A يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه
class C extends A {
printC() {
document.write('Hello from class C <br>');
}
}
// و استدعينا كل الدوال الموجودة فيه b إسمه B هنا قمنا بإنشاء كائن من الكلاس
let b = new B();
b.printA();
b.printB();
// و استدعينا كل الدوال الموجودة فيه c إسمه C هنا قمنا بإنشاء كائن من الكلاس
let c = new C();
c.printA();
c.printC();
// printA يحتوي على دالة إسمهاA هنا قمنا بتعريف كلاس إسمه class A { printA() { document.write('Hello from class A <br>'); } } // printB و يحتوي على دالة إسمها A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه class B extends A { printB() { document.write('Hello from class B <br>'); } } // printC و يحتوي على دالة إسمها A يرث من الكلاس C هنا قمنا بتعريف كلاس إسمه class C extends A { printC() { document.write('Hello from class C <br>'); } } // و استدعينا كل الدوال الموجودة فيه b إسمه B هنا قمنا بإنشاء كائن من الكلاس let b = new B(); b.printA(); b.printB(); // و استدعينا كل الدوال الموجودة فيه c إسمه C هنا قمنا بإنشاء كائن من الكلاس let c = new C(); c.printA(); c.printC();

نتيجة التشغيل:

Hello from class A
Hello from class B
Hello from class A
Hello from class C
جرب الكود

الكلمة المفتاحية super

بشكل عام، نستخدم الكلمة super للتفرقة بين الأشياء الموجودة في الكلاس الإبن و الكلاس الأب إذا كانت تحمل نفس الإسم.

إفتراضياً، مفسر الكود يعطي الأولوية لاستخدام الشيء الموجود في الكلاس الإبن و لكن لو أردنا إعلام مفسر الكود أننا نريد استدعاء الشيء الموجود في الكلاس الأب فإننا نكتب قبله الكلمة super.


طريقة استخدام الكلمة super

فيما يلي طريقة استخدامها لاستدعاء خاصية موجودة في الكلاس الأب بدلاً من استخدام خاصيّة الكلاس الإبن.

super.propertyName

فيما يلي طريقة استخدامها لاستدعاء دالة موجودة في الكلاس الأب بدلاً من استخدام دالة الكلاس الإبن.

super.methodName()

فيما يلي طريقة استخدامها لاستدعاء متغير أو خاصية موجودة في الكلاس الأب.

super()

إذا كان كونستركتور أو دالة الكلاس الأب يحتوي على باراميترات فلا تنسى أن تمرر قيم لها و إلا فإن ذلك سيسبب مشكلة.


إستدعاء كونستركتور الكلاس الأب

بشكل عام، في حال كان الكلاس الأب يحتوي على كونستركتور فإنه لا بد للكلاس الإبن من أن يقوم باستدعائه حتى يعمل الكود بشكل صحيح.

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

يفضّل دائماً أن يتم تعريف الكونستركتور في كل من الكلاس الإبن و الكلاس الأب و لو كان فارغاً حتى تكون العلاقة واضحة بالنسبة للمبرمج إذا ما أراد تعديل الكود في وقت لاحق أو مشاركته مع الغير.


في المثال التالي قمنا بتعريف كونستركتور فارغ الكلاس الأب حتى تلاحظ كيف أنه سيتم استدعاؤه بشكل تلقائي عند إنشاء كائن من الكلاس الإبن.

المثال الأول

// مع تعريف الكونستركتور الخاص به A هنا قمنا بتعريف كلاس إسمه
class A {
constructor() {
document.write('Constructor of class A is called');
}
}
// A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
class B extends A {
// و أصبح يعتمده A فارغ و لكنه فعلياً ورث كونستركتور الكلاس B صحيح أن الكلاس
// B سيتنفذ بشكل تلقائي عند إنشاء كائن من الكلاس A لذلك فإن كونستركتور الكلاس
}
// b إسمه B هنا قمنا بإنشاء كائن من الكلاس
let b = new B();
// مع تعريف الكونستركتور الخاص به A هنا قمنا بتعريف كلاس إسمه class A { constructor() { document.write('Constructor of class A is called'); } } // A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه class B extends A { // و أصبح يعتمده A فارغ و لكنه فعلياً ورث كونستركتور الكلاس B صحيح أن الكلاس // B سيتنفذ بشكل تلقائي عند إنشاء كائن من الكلاس A لذلك فإن كونستركتور الكلاس } // b إسمه B هنا قمنا بإنشاء كائن من الكلاس let b = new B();

نتيجة التشغيل:

Constructor of class A is called
جرب الكود

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

المثال الثاني

// مع تعريف الكونستركتور الخاص به A هنا قمنا بتعريف كلاس إسمه
class A {
// هنا قمنا بتعريف كونستركتور إفتراضي يحتوي على أمر طباعة فقط
constructor() {
document.write('Constructor of class A is called <br>');
}
}
// A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
class B extends A {
// هنا قمنا بتعريف كونستركتور إفتراضي يستدعي كونستركتور الكلاس الأب و يحتوي على أمر طباعة
constructor() {
super();
document.write('Constructor of class B is called <br>');
}
}
// و بالتالي فإنه سيتم b إسمه B هنا قمنا بإنشاء كائن من الكلاس
// B و يليه تنفيذ كونستركتور الكلاس A تنفيذ كونستركتور الكلاس
let b = new B();
// مع تعريف الكونستركتور الخاص به A هنا قمنا بتعريف كلاس إسمه class A { // هنا قمنا بتعريف كونستركتور إفتراضي يحتوي على أمر طباعة فقط constructor() { document.write('Constructor of class A is called <br>'); } } // A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه class B extends A { // هنا قمنا بتعريف كونستركتور إفتراضي يستدعي كونستركتور الكلاس الأب و يحتوي على أمر طباعة constructor() { super(); document.write('Constructor of class B is called <br>'); } } // و بالتالي فإنه سيتم b إسمه B هنا قمنا بإنشاء كائن من الكلاس // B و يليه تنفيذ كونستركتور الكلاس A تنفيذ كونستركتور الكلاس let b = new B();

نتيجة التشغيل:

Constructor of class A is called
Constructor of class B is called
جرب الكود

في المثال السابق، لو لم نستدعي كلاس الكلاس الأب في كونستركتور الكلاس الإبن لظهرت مشكلة في الكونسول و لما تم تنفيذ كونستركتور الكلاس الأب كما يفترض.


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

المثال الثالث

// A هنا قمنا بتعريف كلاس إسمه
class A {
// هنا قمنا بتعريف كونستركتور يحتوي على باراميتر واحد
constructor(x) {
// x القيمة التي يتم تمريرها للكونستركتور سيتم تخزينها في خاصية إسمها
this.x = x;
}
// x هنا قمنا بتعريف دالة تطبع قيمة الخاصية
myMethod() {
document.write('x = ' + this.x + '<br>');
}
}
// A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه
class B extends A {
// هنا قمنا بتعريف كونستركتور يحتوي على ثلاث باراميترات
// x تم تمريرها لكونستركتور الكلاس الأب و الذي سيقوم بتخزينها في الخاصية x القيمة التي يتم تمريرها في الباراميتر
// التي ورثها x إضافةً إلى الخاصية B سيتم تخزينها كخصائص جديدة في الكلاس z و y القيم التي يتم تمريرها للباراميترين
constructor(x, y, z) {
super(x);
this.y = y;
this.z = z;
}
// B هنا قمنا بتعريف دالة تقوم بطباعة جميع قيم الخصائص الموجودة في الكلاس
// x و التي ستقوم بطباعة قيمة A الموجودة في الكلاس myMethod() لاحظ أننا قمنا باستدعاء الدالة
myMethod() {
super.myMethod();
document.write('y = ' + this.y + '<br>');
document.write('z = ' + this.z + '<br>');
}
}
// و تمرير ثلاث قيم له b إسمه B هنا قمنا بإنشاء كائن من الكلاس
let b = new B(10, 20, 30);
// من الكائن لطباعة جميع قيم الخصائص التي يملكها myMethod() هنا قمنا باستدعاء الدالة
b.myMethod()
// A هنا قمنا بتعريف كلاس إسمه class A { // هنا قمنا بتعريف كونستركتور يحتوي على باراميتر واحد constructor(x) { // x القيمة التي يتم تمريرها للكونستركتور سيتم تخزينها في خاصية إسمها this.x = x; } // x هنا قمنا بتعريف دالة تطبع قيمة الخاصية myMethod() { document.write('x = ' + this.x + '<br>'); } } // A يرث من الكلاس B هنا قمنا بتعريف كلاس إسمه class B extends A { // هنا قمنا بتعريف كونستركتور يحتوي على ثلاث باراميترات // x تم تمريرها لكونستركتور الكلاس الأب و الذي سيقوم بتخزينها في الخاصية x القيمة التي يتم تمريرها في الباراميتر // التي ورثها x إضافةً إلى الخاصية B سيتم تخزينها كخصائص جديدة في الكلاس z و y القيم التي يتم تمريرها للباراميترين constructor(x, y, z) { super(x); this.y = y; this.z = z; } // B هنا قمنا بتعريف دالة تقوم بطباعة جميع قيم الخصائص الموجودة في الكلاس // x و التي ستقوم بطباعة قيمة A الموجودة في الكلاس myMethod() لاحظ أننا قمنا باستدعاء الدالة myMethod() { super.myMethod(); document.write('y = ' + this.y + '<br>'); document.write('z = ' + this.z + '<br>'); } } // و تمرير ثلاث قيم له b إسمه B هنا قمنا بإنشاء كائن من الكلاس let b = new B(10, 20, 30); // من الكائن لطباعة جميع قيم الخصائص التي يملكها myMethod() هنا قمنا باستدعاء الدالة b.myMethod()

نتيجة التشغيل:

x = 10
y = 20
z = 30
جرب الكود