يمكننا أيضًا إسناد التوابِع إلى دالة الصنف ذاتها وليس إلى كائن "prototype"
لها. نسمّي هذه التوابِع بالتوابِع ”الثابتة“ (static).
في الأصناف نضع بعدها كلمة static
هكذا:
class User { static staticMethod() { // لاحظ alert(this === User); } } User.staticMethod(); // true
في الواقع، لا يفرق هذا عن إسنادها على أنّها خاصية بشيء:
class User() { } User.staticMethod = function() { alert(this === User); }; User.staticMethod(); // true
وتكون قيمة this
في الاستدعاء User.staticMethod()
هي باني الصنف User
ذاته (تذكّر قاعدة ”الكائن قبل النقطة“),
عادةً ما نستعمل التوابِع الثابتة لكتابة دوال تعود إلى الصنف نفسه وليس إلى أيّ كائن من ذلك الصنف.
مثال للتوضيح: لدينا كائنات مقالات Article
ونريد دالة لموازنتها. الحل الطبيعي هو إضافة تابِع Article.compare
هكذا:
class Article { constructor(title, date) { this.title = title; this.date = date; } // هنا static compare(articleA, articleB) { return articleA.date - articleB.date; } } // الاستعمال let articles = [ new Article("HTML", new Date(2019, 1, 1)), new Article("CSS", new Date(2019, 0, 1)), new Article("JavaScript", new Date(2019, 11, 1)) ]; articles.sort(Article.compare); // لاحظ alert( articles[0].title ); // CSS
نرى هنا التابِع Article.compare
”فوق“ المقالات فيتيح لنا طريقة لموازنتها. ليس التابِع تابِعًا للمقالة ذاتها، بل لصنف المقالات نفسه.
توابِع ”المصانع“ (factory) تنفعها أيضًا التوابِع الثابتة هذه. لنقل بأنّا نريد إنشاء المقالات بطرائق عدّة:
-
بتمرير المُعاملات (العنوان
title
والتاريخdate
وغيرها). - إنشاء مقالة فارغة تحمل تاريخ اليوم.
- أو… كما تريد أنت.
يمكننا تنفيذ الطريقة الأولى باستعمال بانٍ، وللثانية صناعة تابِع ثابت للصنف.
مثل التابِع Article.createTodays()
هنا:
class Article { constructor(title, date) { this.title = title; this.date = date; } static createTodays() { // لا تنسَ: this = Article return new this("Today's digest", new Date()); // موجز أحداث اليوم } } let article = Article.createTodays(); alert( article.title ); // موجز أحداث اليوم
الآن متى أردنا صناعة موجز عن أحداث اليوم، استدعينا Article.createTodays()
. أُعيد، ليس هذا تابِعًا للمقالة نفسها بل تابِعًا للصنف كله.
كما نستعمل التوابِع الثابتة في أصناف قواعد البيانات للبحث عن المُدخلات وحفظها وإزالتها، هكذا:
// بفرض أنّ Article هو صنف مخصّص لإدارة المقالات // نستعمل تابِعًا ثابتًا لإزالة المقالة: Article.remove({id: 12345});
الخاصيات الثابتة
إضافة حديثة
انتبه رجاءً إلى أنه هذه الميزة مضافة حديثًا إلى اللغة، لذا لن تعمل الأمثلة إلى في الإصدارات الحديث من المتصفح كروم.
كما يمكننا أيضًا استعمال الخاصيات الثابتة. ظاهرها فهي خاصيات للأصناف، ولكن نضع قبلها عبارة static
:
class Article { static publisher = "Ilya Kantor"; } alert( Article.publisher ); // Ilya Kantor
لا يفرق هذا عن الإسناد المباشر إلى صنف Article
:
Article.publisher = "Ilya Kantor";
وراثة الخاصيات والتوابِع الثابتة
هذه الأنواع من الخاصيات والتوابِع
فمثلًا التابِع Animal.compare
والخاصية Animal.planet
في الشيفرة أسفله موروثين بالأسماء Rabbit.compare
و Rabbit.planet
:
class Animal { static planet = "Earth"; // الأرض constructor(name, speed) { this.speed = speed; this.name = name; } run(speed = 0) { this.speed += speed; alert(`${this.name} runs with speed ${this.speed}.`); } static compare(animalA, animalB) { return animalA.speed - animalB.speed; } } // نرث من الصنف Animal class Rabbit extends Animal { hide() { alert(`${this.name} hides!`); } } let rabbits = [ new Rabbit("White Rabbit", 10), new Rabbit("Black Rabbit", 5) ]; rabbits.sort(Rabbit.compare); // لاحظ rabbits[0].run(); // Black Rabbit runs with speed 5. alert(Rabbit.planet); // الأرض
الآن متى استدعينا Rabbit.compare
، استدعى المحرّك التابِع Animal.compare
الموروث.
ولكن كيف يعمل هذا الشيء؟ كالعادة، بكائنات prototype: تُقدّم extends
للصنف Rabbit
إشارة [[Prototype]]
إلى الصنف Animal
.
لذا فعبارة Rabbit extends Animal
تصنع إشارتي [[Prototype]]
:
-
دالة
Rabbit
موروثة عبر prototype من دالةAnimal
. -
كائن
Rabbit.prototype
موروث عبر prototype من كائنAnimal.prototype
.
بهذا تعمل الوراثة للتوابِع العادية والثابتة معًا.
تشكّ؟ انظر للشيفرة:
class Animal {} class Rabbit extends Animal {} // لكلّ ما هو ثابت alert(Rabbit.__proto__ === Animal); // true // للتوابِع العادية alert(Rabbit.prototype.__proto__ === Animal.prototype); // true
خلاصة
نستعمل التوابِع الثابتة لأيّة وظائف تخصّ الصنف ”كلّه على بعضه“، ولا يخصّ سيرورة معيّنة من الصنف.
مثل تابع الموازنة Article.compare(article1, article2)
أو تابع المصنع Article.createTodays()
.
ويصنفون كثوابت (static
) في تعريف الصنف.
نستعمل الخاصيات الثابتة متى أردنا تخزين البيانات على مستوى الصنف لا على مستوى السيرورات.
صياغتها هي:
class MyClass { static property = ...; static method() { ... } }
تقنيًا فالتصريح الثابت (static declaration) لا يفرق عن الإسناد إلى الصنف مباشرةً:
MyClass.property = ... MyClass.method = ...
الخاصيات والدوال الثابتة الموروثة.
من أجل العبارة class B extends A
إن prototype للصنف B
يشير إلى A
:B.[[Prototype]] = A
. لذا إن تعذر العثور على خاصية ما في الصنف B
فسيستمر البحث في الصنف A
.
ترجمة -وبتصرف- للفصل Static properties and methods من كتاب The JavaScript language
أفضل التعليقات
لا توجد أية تعليقات بعد
انضم إلى النقاش
يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.