وراثة الفئة هي وسيلة لفئة واحدة لتوسيع فئة أخرى.
حتى نتمكن من إنشاء وظائف جديدة فوق الموجودة.
لنفترض أن لدينا فئة Animal
:
فئة الحيوان { منشئ (الاسم) { this.speed = 0; this.name = name; } تشغيل (السرعة) { this.speed = speed; تنبيه(`${this.name} يعمل بسرعة ${this.speed}.`); } قف() { this.speed = 0; تنبيه(`${this.name} لا يزال قائما.`); } } دع الحيوان = حيوان جديد("حيواني");
إليك كيفية تمثيل الكائن animal
وفئة Animal
بيانياً:
…ونود إنشاء class Rabbit
أخرى.
نظرًا لأن الأرانب حيوانات، يجب أن يعتمد فصل Rabbit
على Animal
، وأن يكون لديه إمكانية الوصول إلى الأساليب الحيوانية، حتى تتمكن الأرانب من القيام بما يمكن للحيوانات "العامة" القيام به.
بناء الجملة لتوسيع فئة أخرى هو: class Child extends Parent
.
لنقم بإنشاء class Rabbit
التي ترث من Animal
:
فئة الأرنب تمتد الحيوان { يخفي() { تنبيه(`${this.name} يختبئ!`); } } دع الأرنب = أرنب جديد("الأرنب الأبيض"); أرنب.تشغيل(5); // الأرنب الأبيض يركض بسرعة 5. hide(); // يختبئ الأرنب الأبيض!
كائن من فئة Rabbit
لديه حق الوصول إلى كل من أساليب Rabbit
، مثل rabbit.hide()
، وأيضًا إلى أساليب Animal
، مثل rabbit.run()
.
داخليًا، تعمل الكلمات الرئيسية extends
باستخدام آليات النماذج الأولية الجيدة. يقوم بتعيين Rabbit.prototype.[[Prototype]]
على Animal.prototype
. لذا، إذا لم يتم العثور على تابع في Rabbit.prototype
، فستأخذه JavaScript من Animal.prototype
.
على سبيل المثال، للعثور على طريقة rabbit.run
، يقوم المحرك بالتحقق (من أسفل إلى أعلى في الصورة):
كائن rabbit
(ليس له run
).
النموذج الأولي الخاص به هو Rabbit.prototype
(يحتوي على hide
ولكن لم يتم run
).
النموذج الأولي الخاص به، وهو (بسبب extends
) Animal.prototype
، والذي يحتوي أخيرًا على طريقة run
.
كما يمكننا أن نتذكر من فصل النماذج الأولية الأصلية، تستخدم JavaScript نفسها وراثة النموذج الأولي للكائنات المضمنة. على سبيل المثال Date.prototype.[[Prototype]]
هو Object.prototype
. ولهذا السبب تتمتع التواريخ بإمكانية الوصول إلى أساليب الكائنات العامة.
يُسمح بأي تعبير بعد extends
يسمح بناء جملة الفئة بتحديد ليس فقط فئة، ولكن أي تعبير بعد extends
.
على سبيل المثال، استدعاء دالة يُنشئ الفئة الأصل:
الدالة و(عبارة) { فئة العودة { sayHi() { تنبيه(عبارة); } }; } يمتد مستخدم الفئة f ("مرحبًا") {} مستخدم جديد().sayHi(); // مرحبًا
هنا class User
يرث من نتيجة f("Hello")
.
قد يكون ذلك مفيدًا لأنماط البرمجة المتقدمة عندما نستخدم الوظائف لإنشاء فئات اعتمادًا على العديد من الشروط ويمكن أن نرثها منها.
الآن دعونا نمضي قدمًا ونتجاوز الطريقة. افتراضيًا، يتم أخذ كافة التوابع غير المحددة في class Rabbit
مباشرةً "كما هي" من class Animal
.
لكن إذا قمنا بتحديد طريقتنا الخاصة في Rabbit
، مثل stop()
فسيتم استخدامها بدلاً من ذلك:
فئة الأرنب تمتد الحيوان { قف() { // ... الآن سيتم استخدام هذا لـ Rabbit.stop() // بدلاً من stop() من فئة Animal } }
ومع ذلك، عادةً، لا نريد استبدال الطريقة الأصلية تمامًا، بل نرغب في البناء عليها لتعديل وظائفها أو توسيعها. نحن نفعل شيئًا ما في طريقتنا، لكن نستدعي الطريقة الأصل قبلها/بعدها أو أثناء العملية.
توفر الفصول كلمة رئيسية "super"
لذلك.
super.method(...)
لاستدعاء الطريقة الأصلية.
super(...)
لاستدعاء المنشئ الأصلي (داخل المنشئ الخاص بنا فقط).
على سبيل المثال، دع أرنبنا يختفي تلقائيًا عند التوقف:
فئة الحيوان { منشئ (الاسم) { this.speed = 0; this.name = name; } تشغيل (السرعة) { this.speed = speed; تنبيه(`${this.name} يعمل بسرعة ${this.speed}.`); } قف() { this.speed = 0; تنبيه(`${this.name} لا يزال قائما.`); } } فئة الأرنب تمتد الحيوان { يخفي() { تنبيه(`${this.name} يختبئ!`); } قف() { super.stop(); // استدعاء توقف الوالدين this.hide(); // ثم قم بالاختباء } } دع الأرنب = أرنب جديد("الأرنب الأبيض"); أرنب.تشغيل(5); // الأرنب الأبيض يركض بسرعة 5. Rabbit.stop(); // الأرنب الأبيض لا يزال قائما. الأرنب الأبيض يختبئ!
أصبح الآن لدى Rabbit
التابع stop
الذي يستدعي التابع super.stop()
الأصلي في هذه العملية.
وظائف السهم ليس لها super
كما ذكرنا في الفصل إعادة النظر في وظائف السهم، لا تحتوي وظائف السهم على super
.
إذا تم الوصول إليها، فهي مأخوذة من الوظيفة الخارجية. على سبيل المثال:
فئة الأرنب تمتد الحيوان { قف() { setTimeout(() => super.stop(), 1000); // استدعاء توقف الوالدين بعد ثانية واحدة } }
إن وظيفة super
الموجودة في وظيفة السهم هي نفسها الموجودة في stop()
، لذا فهي تعمل على النحو المنشود. إذا حددنا وظيفة "عادية" هنا، فسيكون هناك خطأ:
// سوبر غير متوقع setTimeout(function() { super.stop() }, 1000);
مع الصانعين يصبح الأمر صعبًا بعض الشيء.
حتى الآن، لم يكن لدى Rabbit
constructor
خاص به.
وفقًا للمواصفات، إذا قام الفصل بتوسيع فئة أخرى وليس له constructor
، فسيتم إنشاء constructor
"الفارغ" التالي:
فئة الأرنب تمتد الحيوان { // تم إنشاؤها لتوسيع الفئات بدون منشئين خاصين بها منشئ (...وسائط) { سوبر(...args); } }
كما نرى، فهو يستدعي constructor
الأصلي ويمرر جميع الوسائط إليه. يحدث هذا إذا لم نكتب مُنشئًا خاصًا بنا.
الآن دعونا نضيف منشئًا مخصصًا إلى Rabbit
. سيحدد earLength
بالإضافة إلى name
:
فئة الحيوان { منشئ (الاسم) { this.speed = 0; this.name = name; } // ... } فئة الأرنب تمتد الحيوان { منشئ (الاسم، طول الأذن) { this.speed = 0; this.name = name; this.earLength = EarLength; } // ... } // لا يعمل! دع الأرنب = أرنب جديد ("الأرنب الأبيض"، 10)؛ // خطأ: لم يتم تعريف هذا.
عفوًا! لدينا خطأ. الآن لا يمكننا خلق الأرانب. ما الخطأ الذي حدث؟
الجواب القصير هو:
يجب على المنشئين في الفئات الوراثة استدعاء super(...)
و (!) القيام بذلك قبل استخدام this
.
…ولكن لماذا؟ ماذا يحدث هنا؟ والحقيقة أن هذا المطلب يبدو غريباً.
بالطبع، هناك تفسير. دعنا ندخل في التفاصيل، حتى تفهم حقًا ما يحدث.
في JavaScript، هناك فرق بين دالة منشئة لفئة وراثة (ما يسمى "منشئ مشتق") والوظائف الأخرى. يمتلك المنشئ المشتق خاصية داخلية خاصة [[ConstructorKind]]:"derived"
. هذه تسمية داخلية خاصة.
تؤثر هذه التسمية على سلوكها مع new
.
عند تنفيذ دالة عادية باستخدام new
، فإنها تقوم بإنشاء كائن فارغ وتعيينه this
.
ولكن عندما يتم تشغيل مُنشئ مشتق، فإنه لا يفعل ذلك. ويتوقع من المنشئ الأصلي القيام بهذه المهمة.
لذلك يجب على المُنشئ المشتق استدعاء super
من أجل تنفيذ المُنشئ الأصلي (الأساسي)، وإلا فلن يتم إنشاء الكائن الخاص this
. وسنحصل على خطأ.
لكي يعمل مُنشئ Rabbit
، فإنه يحتاج إلى استدعاء super()
قبل استخدام this
، كما هو الحال هنا:
فئة الحيوان { منشئ (الاسم) { this.speed = 0; this.name = name; } // ... } فئة الأرنب تمتد الحيوان { منشئ (الاسم، طول الأذن) { سوبر (الاسم)؛ this.earLength = EarLength; } // ... } // الآن بخير دع الأرنب = أرنب جديد ("الأرنب الأبيض"، 10)؛ تنبيه (rabbit.name)؛ // الأرنب الأبيض تنبيه (rabbit.earLength)؛ // 10
مذكرة متقدمة
تفترض هذه الملاحظة أن لديك خبرة معينة في التعامل مع الفصول الدراسية، ربما في لغات برمجة أخرى.
فهو يوفر رؤية أفضل للغة ويشرح أيضًا السلوك الذي قد يكون مصدرًا للأخطاء (ولكن ليس في كثير من الأحيان).
إذا وجدت صعوبة في الفهم، فما عليك سوى الاستمرار في القراءة، ثم العودة إليها في وقت لاحق.
لا يمكننا تجاوز الأساليب فحسب، بل يمكننا أيضًا تجاوز حقول الفئات.
بالرغم من ذلك، هناك سلوك صعب عندما نصل إلى حقل تم تجاوزه في المنشئ الأصلي، وهو أمر يختلف تمامًا عن معظم لغات البرمجة الأخرى.
خذ بعين الاعتبار هذا المثال:
فئة الحيوان { الاسم = "الحيوان"؛ منشئ () { تنبيه(هذا.اسم); // (*) } } فئة الأرنب تمتد الحيوان { الاسم = "أرنب"؛ } حيوان جديد ()؛ // حيوان أرنب جديد(); // حيوان
هنا، تقوم فئة Rabbit
بتوسيع Animal
وتتجاوز حقل name
بقيمته الخاصة.
لا يوجد منشئ خاص في Rabbit
، لذلك يتم استدعاء منشئ Animal
.
المثير للاهتمام هو أنه في كلتا الحالتين: new Animal()
و new Rabbit()
، فإن alert
الموجود في السطر (*)
يظهر animal
.
بمعنى آخر، يستخدم المنشئ الأصلي دائمًا قيمة الحقل الخاصة به، وليس القيمة التي تم تجاوزها.
ما الغريب في ذلك؟
إذا لم يكن الأمر واضحا بعد، يرجى المقارنة مع الأساليب.
إليك نفس الكود، ولكن بدلًا من الحقل this.name
نسمي الطريقة this.showName()
:
فئة الحيوان { showName() {// بدلاً من this.name = 'animal' تنبيه("حيوان"); } منشئ () { this.showName(); // بدلاً من التنبيه(this.name); } } فئة الأرنب تمتد الحيوان { اسم العرض () { تنبيه ("أرنب")؛ } } حيوان جديد ()؛ // حيوان أرنب جديد(); // أرنب
يرجى ملاحظة: الآن الإخراج مختلف.
وهذا ما نتوقعه بطبيعة الحال. عندما يتم استدعاء المنشئ الأصلي في الفئة المشتقة، فإنه يستخدم الأسلوب المتجاوز.
…ولكن بالنسبة للحقول الصفية فالأمر ليس كذلك. كما ذكرنا سابقًا، يستخدم المُنشئ الأصل دائمًا الحقل الأصلي.
لماذا هناك فرق؟
حسنًا، السبب هو أمر تهيئة الحقل. تتم تهيئة حقل الفصل:
قبل المُنشئ للفئة الأساسية (التي لا تمتد إلى أي شيء)،
مباشرة بعد super()
للفئة المشتقة.
في حالتنا، Rabbit
هو الفئة المشتقة. لا يوجد constructor()
فيه. كما ذكرنا سابقًا، هذا هو نفسه كما لو كان هناك مُنشئ فارغ يحتوي فقط على super(...args)
.
لذلك، يستدعي new Rabbit()
super()
، وبالتالي يتم تنفيذ المُنشئ الأصلي، و(وفقًا لقاعدة الفئات المشتقة) فقط بعد تهيئة حقول الفئة الخاصة به. في وقت تنفيذ المُنشئ الأصلي، لم تكن هناك حقول لفئة Rabbit
حتى الآن، ولهذا السبب يتم استخدام الحقول Animal
.
هذا الاختلاف الدقيق بين الحقول والأساليب خاص بجافا سكريبت.
لحسن الحظ، لا يكشف هذا السلوك عن نفسه إلا إذا تم استخدام حقل تم تجاوزه في المنشئ الأصلي. ثم قد يكون من الصعب فهم ما يحدث، لذلك نحن نشرح ذلك هنا.
إذا أصبحت مشكلة، فيمكن إصلاحها باستخدام الطرق أو الحروف/المحددات بدلاً من الحقول.
معلومات متقدمة
إذا كنت تقرأ البرنامج التعليمي لأول مرة – فقد يتم تخطي هذا القسم.
يتعلق الأمر بالآليات الداخلية وراء الميراث super
.
دعونا نتعمق قليلاً تحت غطاء محرك السيارة super
. سنرى بعض الأشياء المثيرة للاهتمام على طول الطريق.
أولًا، من كل ما تعلمناه حتى الآن، من المستحيل أن يعمل super
على الإطلاق!
نعم، في الواقع، دعونا نسأل أنفسنا، كيف ينبغي أن تعمل من الناحية الفنية؟ عند تشغيل أسلوب كائن، فإنه يحصل على الكائن الحالي بهذا this
. إذا قمنا باستدعاء super.method()
، فسيحتاج المحرك إلى الحصول على method
من النموذج الأولي للكائن الحالي. لكن كيف؟
قد تبدو المهمة بسيطة، ولكنها ليست كذلك. يعرف المحرك الكائن الحالي this
، لذا يمكنه الحصول على method
الأصلية كـ this.__proto__.method
. ولكن من المؤسف أن مثل هذا الحل "الساذج" لن ينجح.
دعونا نظهر المشكلة. بدون فئات، وذلك باستخدام كائنات عادية من أجل البساطة.
يمكنك تخطي هذا الجزء والانتقال أدناه إلى القسم الفرعي [[HomeObject]]
إذا كنت لا تريد معرفة التفاصيل. هذا لن يضر. أو تابع القراءة إذا كنت مهتمًا بفهم الأمور بعمق.
في المثال أدناه، rabbit.__proto__ = animal
. الآن دعونا نحاول: في rabbit.eat()
سنستدعي animal.eat()
باستخدام this.__proto__
:
دع الحيوان = { الاسم: "الحيوان"، يأكل() { تنبيه(`${this.name} يأكل.`); } }; دع الأرنب = { __بروتو__: حيوان، الاسم: "الأرنب"، يأكل() { // هذه هي الطريقة التي يمكن أن يعمل بها super.eat() this.__proto__.eat.call(this); // (*) } }; أرنب.أكل(); // يأكل الأرنب.
في السطر (*)
نأخذ eat
من النموذج الأولي ( animal
) ونسميه في سياق الكائن الحالي. برجاء ملاحظة أن .call(this)
مهم هنا، لأن الدالة this.__proto__.eat()
البسيطة ستنفذ عملية eat
الرئيسية في سياق النموذج الأولي، وليس الكائن الحالي.
وفي الكود أعلاه يعمل بالفعل على النحو المنشود: لدينا alert
الصحيح .
الآن دعونا نضيف كائنًا آخر إلى السلسلة. سنرى كيف تنكسر الأمور:
دع الحيوان = { الاسم: "الحيوان"، يأكل() { تنبيه(`${this.name} يأكل.`); } }; دع الأرنب = { __بروتو__: حيوان، يأكل() { // ...الارتداد بأسلوب الأرنب واستدعاء طريقة الأصل (الحيوان). this.__proto__.eat.call(this); // (*) } }; دع LongEar = { __بروتو__: أرنب، يأكل() { // ... افعل شيئًا بأذنين طويلتين واستدعاء طريقة الوالدين (الأرنب). this.__proto__.eat.call(this); // (**) } }; longEar.eat(); // خطأ: تم تجاوز الحد الأقصى لحجم مكدس الاستدعاءات
الكود لا يعمل بعد الآن! يمكننا أن نرى الخطأ أثناء محاولة استدعاء longEar.eat()
.
قد لا يكون الأمر واضحًا، ولكن إذا تتبعنا استدعاء longEar.eat()
، فيمكننا معرفة السبب. في كلا السطرين (*)
و (**)
قيمة this
هي الكائن الحالي ( longEar
). هذا أمر ضروري: جميع أساليب الكائن تحصل على الكائن الحالي this
، وليس نموذجًا أوليًا أو شيء من هذا القبيل.
لذا، في كلا السطرين (*)
و (**)
قيمة this.__proto__
هي نفسها تمامًا: rabbit
. كلاهما يسميان rabbit.eat
دون الصعود إلى السلسلة في الحلقة التي لا نهاية لها.
وإليكم صورة ما يحدث:
داخل longEar.eat()
، يستدعي السطر (**)
rabbit.eat
ويزوده بـ this=longEar
.
// داخل longEar.eat() لدينا هذا = longEar this.__proto__.eat.call(هذا) // (**) // يصبح longEar.__proto__.eat.call(هذا) // إنه Rabbit.eat.call(this);
ثم في السطر (*)
من rabbit.eat
، نود تمرير المكالمة إلى مستوى أعلى في السلسلة، ولكن this=longEar
، لذا فإن this.__proto__.eat
هو rabbit.eat
مرة أخرى!
// داخل Rabbit.eat() لدينا أيضًا هذا = longEar this.__proto__.eat.call(هذا) // (*) // يصبح longEar.__proto__.eat.call(هذا) // أو (مرة أخرى) Rabbit.eat.call(this);
… لذلك يطلق rabbit.eat
على نفسه اسم الحلقة اللانهائية، لأنه لا يستطيع الصعود أكثر من ذلك.
لا يمكن حل المشكلة باستخدام this
وحده.
[[HomeObject]]
لتوفير الحل، تضيف JavaScript خاصية داخلية خاصة أخرى للوظائف: [[HomeObject]]
.
عندما يتم تحديد دالة كفئة أو طريقة كائن، فإن خاصية [[HomeObject]]
الخاصة بها تصبح ذلك الكائن.
ثم يستخدمه super
لحل النموذج الأولي الأصلي وأساليبه.
دعونا نرى كيف يعمل، أولاً مع الكائنات العادية:
دع الحيوان = { الاسم: "الحيوان"، Eat() { // Animal.eat.[[HomeObject]] == Animal تنبيه(`${this.name} يأكل.`); } }; دع الأرنب = { __بروتو__: حيوان، الاسم: "الأرنب"، Eat() { // Rabbit.eat.[[HomeObject]] == أرنب super.eat(); } }; دع LongEar = { __بروتو__: أرنب، الاسم: "الأذن الطويلة"، Eat() { // longEar.eat.[[HomeObject]] == longEar super.eat(); } }; // يعمل بشكل صحيح longEar.eat(); // تأكل الأذن الطويلة.
إنه يعمل على النحو المنشود، وذلك بفضل ميكانيكا [[HomeObject]]
. تعرف إحدى الطرق، مثل longEar.eat
، [[HomeObject]]
الخاصة بها وتأخذ الطريقة الأصلية من نموذجها الأولي. دون أي استخدام this
.
كما عرفنا من قبل، تكون الوظائف بشكل عام "مجانية"، وليست مرتبطة بالكائنات في JavaScript. لذلك يمكن نسخها بين الكائنات واستدعائها باستخدام كائن آخر this
.
إن مجرد وجود [[HomeObject]]
ينتهك هذا المبدأ، لأن الأساليب تتذكر كائناتها. [[HomeObject]]
لا يمكن تغييره، لذا فإن هذا الارتباط يبقى إلى الأبد.
المكان الوحيد في اللغة الذي يتم فيه استخدام [[HomeObject]]
هو super
. لذا، إذا كانت إحدى الطرق لا تستخدم super
، فلا يزال بإمكاننا اعتبارها مجانية ونسخها بين الكائنات. ولكن مع الأشياء super
قد تسوء.
إليك العرض التوضيحي لنتيجة super
خاطئة بعد النسخ:
دع الحيوان = { قل مرحبا () { تنبيه("أنا حيوان"); } }; // يرث الأرنب من الحيوان دع الأرنب = { __بروتو__: حيوان، قل مرحبا () { super.sayHi(); } }; دع النبات = { قل مرحبا () { تنبيه("أنا نبات"); } }; // ترث الشجرة من النبات دع الشجرة = { __بروتو__: نبات، sayHi: أرنب.sayHi // (*) }; Tree.sayHi(); // أنا حيوان (؟!؟)
يُظهر استدعاء tree.sayHi()
"أنا حيوان". بالتأكيد خطأ.
السبب بسيط:
في السطر (*)
، تم نسخ الطريقة tree.sayHi
من rabbit
. ربما أردنا فقط تجنب تكرار التعليمات البرمجية؟
[[HomeObject]]
الخاص به هو rabbit
، حيث تم إنشاؤه في rabbit
. لا توجد طريقة لتغيير [[HomeObject]]
.
كود tree.sayHi()
يحتوي على super.sayHi()
بالداخل. ويصعد من rabbit
ويأخذ الطريقة من animal
.
وهنا الرسم البياني لما يحدث:
يتم تعريف [[HomeObject]]
للطرق الموجودة في الفئات والكائنات العادية. لكن بالنسبة للكائنات، يجب تحديد الأساليب تمامًا method()
، وليس كـ "method: function()"
.
قد يكون الفرق غير أساسي بالنسبة لنا، ولكنه مهم بالنسبة لجافا سكريبت.
في المثال أدناه، يتم استخدام صيغة غير أسلوبية للمقارنة. لم يتم تعيين خاصية [[HomeObject]]
ولا يعمل الميراث:
دع الحيوان = { أكل: وظيفة () {// الكتابة عمدا مثل هذا بدلا من أكل () {... // ... } }; دع الأرنب = { __بروتو__: حيوان، أكل: وظيفة () { super.eat(); } }; أرنب.أكل(); // خطأ في الاتصال بـ super (لأنه لا يوجد [[HomeObject]])
لتوسيع الفصل الدراسي: class Child extends Parent
:
وهذا يعني أن Child.prototype.__proto__
سيكون Parent.prototype
، لذلك يتم توريث الأساليب.
عند تجاوز المنشئ:
يجب علينا استدعاء المُنشئ الأصلي كـ super()
في مُنشئ Child
قبل استخدام this
.
عند تجاوز طريقة أخرى:
يمكننا استخدام super.method()
في التابع Child
لاستدعاء التابع Parent
.
الداخلية:
تتذكر الطرق فئتها/كائنها في خاصية [[HomeObject]]
الداخلية. هذه هي الطريقة التي يحل بها super
أساليب الوالدين.
لذلك ليس من الآمن نسخ طريقة باستخدام super
من كائن إلى آخر.
أيضًا:
لا تحتوي وظائف السهم على this
أو super
الخاصة بها، لذا فهي تتلاءم بشفافية مع السياق المحيط.
الأهمية: 5
إليك الكود الذي يحتوي على Rabbit
الذي يمتد Animal
.
لسوء الحظ، لا يمكن إنشاء كائنات Rabbit
. ما هو الخطأ؟ أصلحه.
فئة الحيوان { منشئ (الاسم) { this.name = name; } } فئة الأرنب تمتد الحيوان { منشئ (الاسم) { this.name = name; this.created = Date.now(); } } دع الأرنب = أرنب جديد("الأرنب الأبيض"); // خطأ: لم يتم تعريف هذا تنبيه (rabbit.name)؛
وذلك لأن المُنشئ الفرعي يجب أن يتصل بـ super()
.
إليك الكود المصحح:
فئة الحيوان { منشئ (الاسم) { this.name = name; } } فئة الأرنب تمتد الحيوان { منشئ (الاسم) { سوبر (الاسم)؛ this.created = Date.now(); } } دع الأرنب = أرنب جديد("الأرنب الأبيض"); // حسنا الآن تنبيه (rabbit.name)؛ // الأرنب الأبيض
الأهمية: 5
لدينا فئة Clock
. اعتبارًا من الآن، فإنه يطبع الوقت كل ثانية.
ساعة الصف { منشئ ({قالب }) { this.template = template; } يجعل() { اسمحوا التاريخ = تاريخ جديد ()؛ دع الساعات = date.getHours(); إذا (ساعات < 10) ساعات = '0' + ساعات؛ Let mins = date.getMinutes(); إذا (دقيقة < 10) دقيقة = '0' + دقيقة؛ Let secs = date.getSeconds(); إذا (ثانية < 10) ثانية = '0' + ثانية؛ دع الإخراج = this.template .استبدال ('h'، ساعات) .استبدال ('م'، دقيقة) .replace('s', sec); console.log(output); } قف() { ClearInterval(this.timer); } يبدأ() { this.render(); this.timer = setInterval(() => this.render(), 1000); } }
قم بإنشاء فئة جديدة ExtendedClock
ترث من Clock
وتضيف precision
المعلمة - عدد ms
بين "علامات التجزئة". يجب أن يكون 1000
(ثانية واحدة) بشكل افتراضي.
يجب أن يكون الكود الخاص بك موجودًا في الملف extended-clock.js
لا تقم بتعديل clock.js
الأصلي. تمديده.
افتح صندوق الحماية للمهمة.
فئة ExtendedClock تمتد على مدار الساعة { منشئ (خيارات) { سوبر (خيارات)؛ دع {الدقة = 1000} = الخيارات؛ this.precision = الدقة؛ } يبدأ() { this.render(); this.timer = setInterval(() => this.render(), this.precision); } };
افتح الحل في رمل.