يمكننا أيضًا تعيين طريقة للفصل ككل. تسمى هذه الأساليب ثابتة .
في إعلان الفصل، يتم إضافتها مسبقًا بكلمة أساسية static
، مثل هذا:
فئة المستخدم { طريقة ثابتة ثابتة () { تنبيه (هذا === المستخدم)؛ } } User.staticMethod(); // حقيقي
وهذا في الواقع يشبه تعيينه كخاصية مباشرة:
فئة المستخدم { } User.staticMethod = function() { تنبيه (هذا === المستخدم)؛ }; User.staticMethod(); // حقيقي
قيمة this
في استدعاء User.staticMethod()
هي مُنشئ الفئة User
نفسه (قاعدة "الكائن قبل النقطة").
عادة، يتم استخدام الأساليب الثابتة لتنفيذ الوظائف التي تنتمي إلى الفئة ككل، ولكن ليس لأي كائن معين منها.
على سبيل المثال، لدينا كائنات Article
ونحتاج إلى دالة لمقارنتها.
الحل الطبيعي هو إضافة الطريقة الثابتة Article.compare
:
المادة الصفية { منشئ (العنوان والتاريخ) { this.title = title; this.date = date; } مقارنة ثابتة (المادة أ، المادة ب) { إرجاع المادة A.date - المادة B.date؛ } } // الاستخدام دع المقالات = [ مقالة جديدة("HTML"، تاريخ جديد(2019، 1، 1))، مقالة جديدة ("CSS"، تاريخ جديد (2019، 0، 1)))، مقالة جديدة ("جافا سكريبت"، تاريخ جديد (2019، 11، 1)) ]; Articles.sort(Article.compare); تنبيه( المقالات[0].العنوان); // CSS
هنا طريقة Article.compare
تقف على المقالات "أعلاه"، كوسيلة للمقارنة بينها. إنها ليست طريقة لمقال، بل للفصل بأكمله.
مثال آخر سيكون ما يسمى بطريقة "المصنع".
لنفترض أننا نحتاج إلى طرق متعددة لإنشاء مقال:
إنشاء بواسطة معلمات معينة ( title
date
وما إلى ذلك).
أنشئ مقالة فارغة بتاريخ اليوم.
… أو بطريقة أو بأخرى.
الطريقة الأولى يمكن تنفيذها من قبل المنشئ. وبالنسبة للطريقة الثانية يمكننا عمل طريقة ثابتة للفئة.
مثل Article.createTodays()
هنا:
المادة الصفية { منشئ (العنوان والتاريخ) { this.title = title; this.date = date; } ثابت createTodays () { // تذكر، هذا = Article return new this("ملخص اليوم", new Date()); } } دع المادة = Article.createTodays(); تنبيه(article.title); // ملخص اليوم
الآن، في كل مرة نحتاج فيها إلى إنشاء ملخص اليوم، يمكننا استدعاء Article.createTodays()
. مرة أخرى، هذه ليست طريقة للمقالة، ولكنها طريقة للفصل بأكمله.
تُستخدم الأساليب الثابتة أيضًا في الفئات المرتبطة بقاعدة البيانات للبحث/الحفظ/إزالة الإدخالات من قاعدة البيانات، مثل هذا:
// بافتراض أن المقالة هي فئة خاصة لإدارة المقالات // طريقة ثابتة لإزالة المقالة بالمعرف: إزالة المادة({المعرف: 12345});
الأساليب الثابتة غير متاحة للكائنات الفردية
الأساليب الثابتة قابلة للاستدعاء على الفئات، وليس على الكائنات الفردية.
على سبيل المثال، لن يعمل هذا الرمز:
// ... Article.createTodays(); /// خطأ: Article.createTodays ليس دالة
إضافة حديثة
هذه إضافة حديثة للغة. تعمل الأمثلة في متصفح Chrome الأخير.
الخصائص الثابتة ممكنة أيضًا، فهي تبدو كخصائص فئة عادية، ولكنها مُلحقة مسبقًا بـ static
:
المادة الصفية { الناشر الثابت = "ايليا كانتور"؛ } تنبيه(المادة.الناشر); // ايليا كانتور
وهذا هو نفس الإحالة المباشرة إلى Article
:
Article.publisher = "ايليا كانتور"؛
الخصائص والأساليب الثابتة موروثة.
على سبيل المثال، Animal.compare
و Animal.planet
في الكود أدناه موروثان ويمكن الوصول إليهما كـ Rabbit.compare
و Rabbit.planet
:
فئة الحيوان { كوكب ثابت = "الأرض"; منشئ (الاسم والسرعة) { this.speed = speed; this.name = name; } تشغيل (السرعة = 0) { this.speed += speed; تنبيه(`${this.name} يعمل بسرعة ${this.speed}.`); } مقارنة ثابتة (حيوان أ، حيوان ب) { إرجاع AnimalA.speed - AnimalB.speed؛ } } // وراثة من الحيوان فئة الأرنب تمتد الحيوان { يخفي() { تنبيه(`${this.name} يختبئ!`); } } دع الأرانب = [ أرنب جديد ("الأرنب الأبيض"، 10)، أرنب جديد("الأرنب الأسود"، 5) ]; Rabbits.sort(Rabbit.compare); الأرانب[0].run(); // الأرنب الأسود يركض بسرعة 5. تنبيه (Rabbit.planet)؛ // أرض
الآن عندما نسمي Rabbit.compare
، سيتم استدعاء Animal.compare
الموروث.
كيف يعمل؟ مرة أخرى، باستخدام النماذج الأولية. كما كنت قد خمنت بالفعل، extends
تمنح Rabbit
مرجع [[Prototype]]
إلى Animal
.
لذلك، Rabbit extends Animal
بإنشاء مرجعين [[Prototype]]
:
وظيفة Rabbit
ترث نموذجيًا من وظيفة Animal
.
نموذج Rabbit.prototype
يرث من Animal.prototype
.
ونتيجة لذلك، فإن الميراث يعمل لكل من الطرق العادية والثابتة.
هنا، دعونا نتحقق من ذلك عن طريق الكود:
فئة الحيوان {} فئة الأرنب تمتد الحيوان {} // للإحصائيات تنبيه (Rabbit.__proto__ === حيوان)؛ // حقيقي // للطرق العادية تنبيه(Rabbit.prototype.__proto__ === Animal.prototype); // حقيقي
يتم استخدام الأساليب الثابتة للوظيفة التي تنتمي إلى الفئة "ككل". لا يتعلق الأمر بمثيل فئة محددة.
على سبيل المثال، طريقة المقارنة Article.compare(article1, article2)
أو طريقة المصنع Article.createTodays()
.
يتم تصنيفها بواسطة الكلمة static
في إعلان الفصل.
تُستخدم الخصائص الثابتة عندما نرغب في تخزين بيانات على مستوى الفصل الدراسي، ولا ترتبط أيضًا بمثيل.
بناء الجملة هو:
فئة ماي كلاس { خاصية ثابتة = ...; طريقة ثابتة () { ... } }
من الناحية الفنية، الإعلان الثابت هو نفس التعيين للفئة نفسها:
MyClass.property = ... طريقة MyClass = ...
الخصائص والأساليب الثابتة موروثة.
بالنسبة class B extends A
يشير النموذج الأولي للفئة B
نفسها إلى A
: B.[[Prototype]] = A
. لذا، إذا لم يتم العثور على حقل في B
، فسيستمر البحث في A
الأهمية: 3
كما نعلم، ترث جميع الكائنات عادةً من Object.prototype
وتتمكن من الوصول إلى أساليب الكائنات "العامة" مثل hasOwnProperty
وما إلى ذلك.
على سبيل المثال:
فئة الأرنب { منشئ (الاسم) { this.name = name; } } دع الأرنب = أرنب جديد("Rab"); // طريقة hasOwnProperty هي من Object.prototype تنبيه (أرنب. hasOwnProperty('name') ); // حقيقي
ولكن إذا قمنا بتوضيحها بشكل صريح مثل "class Rabbit extends Object"
، فستكون النتيجة مختلفة عن "class Rabbit"
البسيط؟
ما الفرق؟
فيما يلي مثال على هذا الكود (لا يعمل - لماذا؟ أصلحه؟):
فئة الأرنب تمتد الكائن { منشئ (الاسم) { this.name = name; } } دع الأرنب = أرنب جديد("Rab"); تنبيه (أرنب. hasOwnProperty('name') ); // خطأ
أولاً، دعونا نرى لماذا لا يعمل الكود الأخير.
يصبح السبب واضحًا إذا حاولنا تشغيله. يجب على منشئ الفئة الوراثة استدعاء super()
. وإلا فلن يتم "this"
".
إذن هذا هو الإصلاح:
فئة الأرنب تمتد الكائن { منشئ (الاسم) { ممتاز()؛ // تحتاج إلى استدعاء المُنشئ الأصلي عند الوراثة this.name = name; } } دع الأرنب = أرنب جديد("Rab"); تنبيه (أرنب. hasOwnProperty('name') ); // حقيقي
ولكن هذا ليس كل شيء بعد.
حتى بعد الإصلاح، لا يزال هناك فرق مهم بين "class Rabbit extends Object"
و class Rabbit
.
كما نعلم، فإن بناء الجملة "يمتد" ينشئ نموذجين أوليين:
بين "prototype"
لوظائف المنشئ (للطرق).
بين وظائف المنشئ نفسها (للطرق الثابتة).
في حالة class Rabbit extends Object
فهذا يعني:
فئة الأرنب تمتد الكائن {} تنبيه( Rabbit.prototype.__proto__ === Object.prototype ); // (1) صحيح تنبيه (Rabbit.__proto__ === كائن)؛ // (2) صحيح
لذا يوفر Rabbit
الآن إمكانية الوصول إلى الأساليب الثابتة Object
عبر Rabbit
، مثل هذا:
فئة الأرنب تمتد الكائن {} // عادةً ما نسميه Object.getOwnPropertyNames تنبيه ( Rabbit.getOwnPropertyNames({أ: 1, ب: 2})); // أ، ب
ولكن إذا لم يكن لدينا extends Object
، فلن يتم تعيين Rabbit.__proto__
على Object
.
وهنا العرض التوضيحي:
فئة الأرنب {} تنبيه( Rabbit.prototype.__proto__ === Object.prototype ); // (1) صحيح تنبيه (Rabbit.__proto__ === كائن)؛ // (2) خطأ (!) تنبيه( Rabbit.__proto__ === Function.prototype ); // كأي وظيفة بشكل افتراضي // خطأ، لا توجد مثل هذه الوظيفة في Rabbit تنبيه ( Rabbit.getOwnPropertyNames({أ: 1, ب: 2})); // خطأ
لذلك لا يوفر Rabbit
الوصول إلى الأساليب الثابتة Object
في هذه الحالة.
بالمناسبة، يحتوي Function.prototype
أيضًا على أساليب وظيفية "عامة"، مثل call
و bind
وما إلى ذلك. وهي متاحة في النهاية في كلتا الحالتين، لأنه بالنسبة لمنشئ Object
المدمج، Object.__proto__ === Function.prototype
.
ها هي الصورة:
لذا، باختصار، هناك اختلافان:
أرنب الصف | يقوم فئة الأرنب بتوسيع الكائن |
---|---|
- | يحتاج إلى استدعاء super() في المنشئ |
Rabbit.__proto__ === Function.prototype | Rabbit.__proto__ === Object |