في المقالة السابقة ، تم إدخال مفهوم النموذج الأولي والعلاقة بين أصدقاء المُنشئين الثلاثة ، وكائن النموذج الأولي ، والمثيل في JavaScript: كل مُنشئ له "وصي" - كائن النموذج الأولي ، وهناك أيضًا "موضع" للمركبة في قلب كائن النموذج الأولي. الاثنان في حالة حب ، لكن المثال "في الحب سرا" مع كائن النموذج الأولي ، كما أنها تحافظ على موضع كائن النموذج الأولي في قلبها.
JavaScript نفسها ليست لغة موجهة نحو الكائن ، ولكن لغة قائمة على الكائن. بالنسبة لأولئك الذين اعتادوا على لغات OO الأخرى ، فإنه غير مريح بعض الشيء في البداية لأنه لا يوجد مفهوم لـ "الفصل" هنا ، أو لا يوجد تمييز بين "الفئة" و "مثيل" ، ناهيك عن الفرق بين "الفئة الأم" و "الفئة الفرعية". لذا ، كيف يتم ربط أكوام الكائنات هذه في JavaScript بهذه الطريقة؟
لحسن الحظ ، قدمت JavaScript طريقة تنفيذ "الميراث" في بداية تصميمها. قبل فهم "الميراث" ، سنفهم الآن مفهوم سلاسل النموذج الأولي.
سلسلة النموذج الأولي
نحن نعلم أن النماذج الأولية لها مؤشر للمؤشر. ماذا لو جعلنا كائن النموذج الأولي من الفئة الفرعية مساوياً لمثيل آخر من الطبقة الفائقة الجديدة ()؟ في هذا الوقت ، يحتوي كائن النموذج الأولي للفئة الفرعية على مؤشر إلى النموذج الأولي للطبقة الفائقة ، ويحتوي النموذج الأولي الفائق أيضًا على مؤشر إلى مُنشئ الطبقة الفائقة. . . وبهذه الطريقة ، تتشكل سلسلة النموذج الأولي.
الرمز المحدد كما يلي:
الدالة superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma girl!" ؛ } الدالة الفرعية () {this.subname = "أختك" ؛ } الفئة الفرعية subcleass.prototype.subsaywhat = function () {return this.subname + ": i`ma beautiful girl" ؛ } var sub = فئة فرعية جديدة () ؛ console.log (sub.saywhat ()) ؛ // النساء: أنا فتاة!استخدم سلسلة النموذج الأولي لتحقيق الميراث
من الكود أعلاه ، يمكننا أن نرى أن الفئة الفرعية ترث خصائص وطرق الطبقة الفائقة. هذا التنفيذ الموروثة هو عن طريق تعيين مثيل الفئة الفائقة إلى كائن النموذج الأولي من الفئة الفرعية. وبهذه الطريقة ، يتم كتابة كائن النموذج الأولي للفئة الفرعية بواسطة مثيل من الفئة الفائقة ، ويكون له كل خصائصه وطرقه ، وكذلك وجود مؤشر إلى كائن النموذج الأولي للطبقة الفائقة.
هناك بعض الأشياء التي يجب الانتباه إليها عند تنفيذ الميراث باستخدام سلاسل النموذج الأولي:
انتبه إلى التغييرات في المنشئ بعد الميراث. هنا مُنشئ النقاط الفرعية إلى الفئة الفائقة لأن النموذج الأولي للفئة الفرعية يشير إلى الفئة الفائقة. عند فهم سلسلة النموذج الأولي ، لا تتجاهل كائن الكائن الافتراضي في النهاية ، ولهذا السبب يمكننا استخدام طرق مدمجة مثل tostring في جميع الكائنات.
عند تنفيذ الميراث من خلال سلسلة النموذج الأولي ، لا يمكنك استخدام التعريف الحرفي لطريقة النموذج الأولي ، لأن هذا سيعيد كتابة كائن النموذج الأولي (تم تقديمه أيضًا في المقالة السابقة):
الدالة superclass () {this.name = "women"} superclass.prototype.saywhat = function () {return this.name + ": i`ma girl!" ؛ } الدالة الفرعية () {this.subname = "أختك" ؛ } الفئة الفرعية subcleass.prototype = {// كائن النموذج الأولي يكتب هنا لأن سمات الطبقة الفائقة والأساليب لا يمكن مورراها subsaywhat: function () {return this.subname + ": i`ma beautiful girl" ؛ }} var sub = فئة فرعية جديدة () ؛ console.log (sub.saywhat ()) ؛ // typeerror: غير محدد ليست وظيفةمشكلة مع مشاركة مثيل. عند شرح النموذج الأولي والمُنشئ في وقت سابق ، قدمنا ذات مرة أن النماذج الأولية التي تحتوي على سمات نوع مرجعية ستشاركها جميع الحالات. وبالمثل ، سيتم أيضًا مشاركة خصائص النوع المرجعي في النموذج الأولي "الفئة الأصل" في النموذج الأولي. عندما نقوم بتعديل سمات النوع المرجعي لـ "فئة الأصل" من خلال ميراث النموذج الأولي ، ستتأثر جميع الحالات الأخرى الموروثة من النموذج الأولي. هذا لا يضيع الموارد فحسب ، بل أيضًا ظاهرة لا نريد رؤيتها:
وظيفة Superclass () {this.name = "Women" ؛ this.bra = ["A" ، "B"] ؛ } الدالة الفرعية () {this.subname = "أختك" ؛ } الفئة الفرعية var sub1 = فئة فرعية جديدة () ؛ sub1.name = "man" ؛ sub1.bra.push ("C") ؛ console.log (sub1.name) ؛ // man console.log (sub1.bra) ؛ // ["a" ، "b" ، "c"] var sub2 = new subclass () ؛ console.log (sub1.name) ؛ // woman console.log (sub2.bra) ؛ // ["A" ، "B" ، "C"]ملاحظة: أضف عنصرًا إلى الصفيف هنا ، ستتأثر جميع الحالات الموروثة من الفئة الفائقة ، ولكن إذا قمت بتعديل سمة الاسم ، فلن يؤثر ذلك على مثيلات أخرى ، لأن الصفيف هو نوع مرجعي واسم هو نوع بدائي.
كيف تحل مشكلة مشاركة المثيل؟ دعنا نستمر في النظر إلى أسفل ...
الميراث الكلاسيكي (سرقة المنشئ)
كما قدمنا أننا نادراً ما نستخدم النماذج الأولية لتحديد الكائنات وحدها ، في التطوير الفعلي ، نادراً ما نستخدم سلاسل النموذج الأولي وحده. من أجل حل مشكلة مشاركة الأنواع المرجعية ، قام مطورو JavaScript بتقديم نمط الميراث الكلاسيكي (بعض الأشخاص يطلقون على ميراث مُنشئ مستعار). تنفيذها بسيط للغاية لاستدعاء مُنشئات SuperType في منشئي النوع الفرعي. نحتاج إلى استخدام الدالة Call () أو تطبيق () التي توفرها JavaScript ، دعنا نلقي نظرة على المثال:
وظيفة Superclass () {this.name = "Women" ؛ this.bra = ["A" ، "B"] ؛} الوظيفة الفرعية () {this.subname = "أختك" ؛ // قم بتعيين نطاق الفئة الفائقة إلى المنشئ الحالي لتنفيذ ميراث superclass.call (هذا) ؛} var sub1 = فئة فرعية جديدة () ؛ sub1.bra.push ("c") ؛ console.log (sub1.bra) ؛ // الفئة الفرعية () ؛ console.log (sub2.bra) ؛ // ["A" ، "B"]superclass.call (هذا) ؛ تعني هذه الجملة أن أعمال التهيئة لمؤسسة الفئة الفائقة تسمى بيئة (السياق) من الفئة الفرعية ، بحيث يكون لكل مثيل نسخته الخاصة من سمة حمالة الصدر ، والتي لن يكون لها أي تأثير على بعضها البعض.
ومع ذلك ، فإن طريقة التنفيذ هذه لا تزال غير مثالية. منذ تقديم المُنشئ ، نواجه أيضًا المشكلة مع المُنشئ المذكورة في المقالة السابقة: إذا كان هناك تعريف طريقة في المُنشئ ، فهناك إشارة منفصلة عن أي من الحالات. هدفنا هو مشاركة هذه الطريقة ، ولا يمكن استدعاء الطرق التي نحددها في النموذج الأولي SuperType في مثيل النوع الفرعي:
وظيفة Superclass () {this.name = "Women" ؛ this.bra = ["A" ، "B"] ؛ } superclass.prototype.saywhat = function () {console.log ("hello") ؛ } الدالة الفرعية () {this.subname = "أختك" ؛ superclass.call (هذا) ؛ } var sub1 = فئة فرعية جديدة () ؛ console.log (sub1.saywhat ()) ؛ // typeerror: غير محدد ليست وظيفةإذا كنت قد قرأت المقالة السابقة حول كائنات النموذج الأولي والمقدمات ، فيجب أن تعرف بالفعل الإجابة لحل هذه المشكلة ، أي اتبع روتين المقالة السابقة واستخدام "Complet Punch"!
الجمع بين الميراث
الجمع بين الميراث هو وسيلة للجمع بين مزايا سلسلة النموذج الأولي والمؤسس ، ودمجها لتحقيق الميراث. ببساطة ، هو استخدام سلسلة النموذج الأولي لورث السمات والأساليب ، واستخدام المنشآت المستعارة لتنفيذ ميراث سمات المثال. هذا لا يحل فقط مشكلة مشاركة سمات المثيل ، ولكن أيضًا يتيح أن يتم مورث سمات وأساليب من النوع الفائق:
وظيفة Superclass () {this.name = "Women" ؛ this.bra = ["A" ، "B"] ؛ } superclass.prototype.saywhat = function () {console.log ("hello") ؛ } الدالة الفرعية () {this.subname = "أختك" ؛ superclass.call (هذا) ؛ // الدعوة الثانية لـ Superclass} الفئة الفرعية. // أول مكالمة إلى Superclass var sub1 = فئة فرعية جديدة () ؛ console.log (sub1.saywhat ()) ؛ //مرحبًاطريقة الميراث المركب هي أيضًا الطريقة الأكثر استخدامًا لتنفيذ الميراث في التطوير الفعلي. في هذه المرحلة ، يمكن أن تلبي احتياجات التنمية الفعلية الخاصة بك ، ولكن سعي الناس للكمال لا نهاية له ، لذلك سيكون هناك حتما شخص "يجد" حول هذا النمط: لقد دعا النمط الخاص بك مُنشئ النوع الفائق مرتين! مرتين. . . هل صنعته؟ هل هذا تضخيم 100 ضعف فقدان الأداء؟
أقوى دحض هو التوصل إلى حل ، لكن لحسن الحظ ، وجد المطور أفضل حل لهذه المشكلة:
الجمع الطفيلي الميراث
قبل تقديم طريقة الميراث هذه ، نفهم أولاً مفهوم المنشئ الطفيلي. يشبه المنشئ الطفيلي نمط المصنع المذكور أعلاه. فكرتها هي تحديد وظيفة مشتركة. يتم استخدام هذه الوظيفة خصيصًا للتعامل مع إنشاء كائن. بعد الانتهاء من الإنشاء ، يعيد هذا الكائن. هذه الوظيفة تشبه إلى حد كبير مُنشئ ، لكن المُنشئ لا يعيد قيمة:
دالة gf (name ، bra) {var obj = new Object () ؛ obj.name = الاسم ؛ obj.bra = bra ؛ obj.saywhat = function () {console.log (this.name) ؛ } return obj ؛} var gf1 = new gf ("bingbing" ، "c ++") ؛ console.log (gf1.saywhat ()) ؛ // bingbingيشبه تنفيذ الميراث الطفيلي المنشئ الطفيلي. إنه ينشئ دالة "مصنع" لا تعتمد على أنواع محددة ، ويتناول بشكل خاص عملية ميراث الكائن ، ثم إرجاع مثيل الكائن الموروثة. لحسن الحظ ، هذا لا يتطلب منا تنفيذها بأنفسنا. لقد زودنا Dao GE (Douglas) منذ فترة طويلة طريقة تنفيذ:
كائن الدالة (obj) {function f () {} f.prototype = obj ؛ إرجاع جديد f () ؛} var superclass = {name: "bingbing" ، bra: "c ++"} var subcleas = object (superclass) ؛ console.log (subcleas.name) ؛ // bingbingيتم توفير مُنشئ بسيط في دالة عامة ، ثم يتم تعيين مثيل للكائن الذي تم تمريره إلى كائن النموذج الأولي للمُنشئ ، وأخيراً إعادة مثيل المُنشئ أمر بسيط للغاية ، لكن الفعالية جيدة جدًا ، أليس كذلك؟ تسمى هذه الطريقة لاحقًا "ميراث النموذج الأولي" ، ويتم تحقيق الميراث الطفيلي بناءً على النموذج الأولي من خلال تعزيز الخصائص المخصصة للكائن:
دالة buildObj (obj) {var o = object (obj) ؛ O.Saywhat = function () {console.log ("Hello") ؛ } return o ؛} var superclass = {name: "bingbing" ، bra: "c ++"} var gf = buildobj (superclass) ؛ gf.saywhat () ؛ // helloيواجه الوراثة الطفيلية أيضًا مشكلة إعادة استخدام الوظيفة في النماذج الأولية ، لذلك بدأ الناس في تجميع اللبنات الأساسية مرة أخرى ، وُلدت الوراثة الجمعية الطفيلية ، مع الغرض من حل مشكلة استدعاء مُنشئ نوع الوالدين عند تحديد النموذج الأولي من النوع الفرعي ، وفي الوقت نفسه ، لزيادة إعادة استخدام الوظيفة. بناءً على أساليب التنفيذ الأساسية أعلاه هي كما يلي:
. // respecify سمة مُنشئ من مثيل proto proto.constructor = sub ؛ // قم بتعيين الكائن الذي تم إنشاؤه إلى النموذج الأولي للنوع الفرعي الفرعي. this.bra = ["a" ، "b"] ؛} superclass.prototype.saywhat = function () {console.log ("hello") ؛} الوظيفة الفرعية () {this.name = "women" ؛ this.bra = ["a" ، "b"] ؛} superclass.prototype.saywhat = function () {console.log ("hello") ؛} الدالة الفرعية () {this.subname = "أختك" ؛ superclass.call (this) ؛} errinitObj (الفئة الفرعية ، الفئة الفائقة) ؛ var sub1 = فئة فرعية جديدة () ؛ console.log (sub1.saywhat ()) ؛ //مرحبًايتجنب هذا التنفيذ مكالمتين إلى supertypes ، ويوفر أيضًا خصائص غير ضرورية على الفئة الفرعية. النموذج ، ويحافظ أيضًا على سلسلة النموذج الأولي. في هذه المرحلة ، انتهت رحلة الميراث حقًا ، وقد أصبح هذا التنفيذ أيضًا طريقة تنفيذ الميراث المثالية! لا يزال جدل الناس حول ميراث جافا سكريبت مستمرًا. بعض المدافعين عن العمل ، وبعضهم يعارضون بذل جهود غير ضرورية في جافا سكريبت لتحقيق خصائص OO. ماذا لو كان ، على الأقل لدي فهم أعمق!