close
١٥ ديسمبر ٢٠٢١

الخواص والدوال الثابتة

كما يمكننا تعيين خاصية لدالة الclass ذاتها, وليس لـ "prototype" الخاص بها. مثل هذه الدوال تسمى بـstatic.

في الـ class, يتم إلحاقهم بكلمة رئيسية 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 ()هي مُنشئ الفئةالمستخدم نفسه (قاعدة “object قبل النقطة”).

عادة ، يتم استخدام الأساليب الثابتة لتنفيذ الوظائف التي تنتمي إلى الفئة ، ولكن ليس لأي object معين منها.

على سبيل المثال ، لدينا objects Article ونحتاج إلى وظيفة لمقارنتها. الحل الطبيعي هو إضافة طريقة Article.compare ، على النحو التالي:

class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static compare(articleA, articleB) {
    return articleA.date - articleB.date;
  }
}

// usage
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” تقف المقالات “أعلاه” ، كوسيلة لمقارنتها. إنها ليست دالة لـ article ، ولكن بدلاً من الـ class بأكمله.

مثال آخر هو ما يسمى طريقة “المصنع”. تخيل ، نحن بحاجة إلى طرق قليلة لإنشاء مقال:

  1. إنشاء بواسطة معلمات معينة (العنوان ،التاريخ وما إلى ذلك).
  2. إنشاء مقال فارغ بتاريخ اليوم.
  3. … أو بطريقة أخرى.

يمكن تنفيذ الطريقة الأولى من قبل المنشئ. وللثاني يمكننا عمل طريقة ثابتة للفئة.

مثل Article.createTodays() هنا:

class Article {
  constructor(title, date) {
    this.title = title;
    this.date = date;
  }

  static createTodays() {
    // remember, this = Article
    return new this("Today's digest", new Date());
  }
}

let article = Article.createTodays();

alert( article.title ); // Today's digest

الآن في كل مرة نحتاج فيها إلى إنشاء ملخص اليوم ، يمكننا استدعاء ``Article.createTodays ()`. مرة أخرى ، هذه ليست طريقة لمقالة ، ولكنها طريقة للفصل بأكمله.

يتم استخدام الأساليب الثابتة أيضًا في الفئات المتعلقة بقاعدة البيانات للبحث / حفظ / إزالة الإدخالات من قاعدة البيانات ، مثل هذا:

// assuming Article is a special class for managing articles
// static method to remove the article:
Article.remove({ id: 12345 });

Static properties

إضافة حديثة
هذه إضافة حديثة إلى اللغه. الإمثله تعمل فى النسخه الحديثه من Chrome.

الخصائص الثابتة ممكنة أيضًا ، فهي تبدو مثل خصائص الفئة العادية ، ولكن يتم إلحاقها بـ static:

class Article {
  static publisher = 'Ilya Kantor';
}

alert(Article.publisher); // Ilya Kantor

That is the same as a direct assignment to Article:

Article.publisher = 'Ilya Kantor';

Inheritance of static properties and methods

الخصائص والأساليب الثابتة موروثة.

على سبيل المثال ، 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;
  }

}

// Inherit from 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); // Earth

الآن عندما نسمي “Rabbit.compare” ، سيتم استدعاء “Animal.compare” الموروث.

كيف يعمل؟ مرة أخرى ، باستخدام النماذج الأولية. كما كنت قد خمنت بالفعل ، فإن كلمة “يمتد” تعطي كلمة “أرنب” يشير "[[نموذج أولي]]` إلى “حيوان”.

Image

So, Rabbit extends Animal creates two [[Prototype]] references:

  1. Rabbit function prototypally inherits from Animal function.
  2. Rabbit.prototype prototypally inherits from Animal.prototype.

As a result, inheritance works both for regular and static methods.

Here, let’s check that by code:

class Animal {}
class Rabbit extends Animal {}

// for statics
alert(Rabbit.__proto__ === Animal); // true

// for regular methods
alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

ملخص

يتم استخدام الأساليب الثابتة للوظيفة التي تنتمي إلى الفئة “ككل”. لا يتعلق الأمر بمثيل فئة ملموسة.

على سبيل المثال ، طريقة للمقارنة Article.compare (article1 ، article2) أو طريقة مصنع Article.createTodays ().

يتم تسميتها بكلمة “ثابت” في إعلان الفئة.

يتم استخدام الخصائص الثابتة عندما نرغب في تخزين البيانات على مستوى الفصل الدراسي ، والتي لا ترتبط أيضًا بمثيل.

الصيغة هي:

class MyClass {
  static property = ...;

  static method() {
    ...
  }
}

من الناحية الفنية ، فإن الإعلان الثابت هو نفسه التخصيص للـ class نفسها:

MyClass.property = ...
MyClass.method = ...

الخصائص والأساليب الثابتة موروثة.

بالنسبة إلى “الفئة B التي تمد A” ، يشير النموذج الأولي للفئة B نفسها إلىA: B. [[Prototype]] = A. لذا ، إذا لم يتم العثور على حقل في B ، فسيستمر البحث فيA.

مهمه

كما نعلم ، عادة ما ترث جميع الكائنات من Object.prototype وتحصل على طرق للكائنات" العامة "مثلhasOwnProperty وما إلى ذلك.

على سبيل المثال:

class Rabbit {
  constructor(name) {
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

// hasOwnProperty method is from Object.prototype
alert( rabbit.hasOwnProperty('name') ); // true

ولكن إذا وضحناها صراحة مثل “class class rabbit Extended Object” ، فستكون النتيجة مختلفة عن “class rabbit” بسيطة؟

ماهو الفرق؟

فيما يلي مثال لمثل هذا الرمز (لا يعمل – لماذا؟ إصلاحه؟):

class Rabbit extends Object {
  constructor(name) {
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

alert( rabbit.hasOwnProperty('name') ); // Error

أولاً ، دعنا نرى لماذا لا يعمل الكود الأخير.

يصبح السبب واضحًا إذا حاولنا تشغيله. يجب على مُنشئ الفصل الموروث استدعاء `` super () `. وإلا فلن يتم تحديد “هذا” ".

إذن هذا هو الإصلاح:

class Rabbit extends Object {
  constructor(name) {
    super(); // need to call the parent constructor when inheriting
    this.name = name;
  }
}

let rabbit = new Rabbit("Rab");

alert( rabbit.hasOwnProperty('name') ); // true

لكن هذا ليس كل شيء بعد.

حتى بعد الإصلاح ، لا يزال هناك اختلاف مهم في “class rabbit يوسع الكائن” “مقابل” class Rabbit ".

كما نعلم ، فإن الصيغة “الممتدة” تضع نموذجين أوليين:

  1. بين “النموذج” لوظائف المنشئ (للطرق).
  2. بين وظائف المنشئ أنفسهم (للأساليب الثابتة).

في حالتنا ، تعني كلمة “أرنب يمتد الكائن” ما يلي:

class Rabbit extends Object {}

alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) true

إذن يوفر “الأرنب” الآن إمكانية الوصول إلى الأساليب الثابتة لـ “الكائن” عبر “الأرنب” ، على النحو التالي:

class Rabbit extends Object {}

// normally we call Object.getOwnPropertyNames
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // a,b

ولكن إذا لم يكن لدينا Extended Object ، فلن يتم تعيينRabbit .__ proto__ على Object.

هنا هو العرض التوضيحي:

class Rabbit {}

alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true
alert( Rabbit.__proto__ === Object ); // (2) false (!)
alert( Rabbit.__proto__ === Function.prototype ); // as any function by default

// error, no such function in Rabbit
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // Error

لذا Rabbit لا يوفر الوصول إلى الأساليب الثابتة لـ “الكائن” في هذه الحالة.

بالمناسبة ، يحتوي Function.prototype على طرق وظيفية" عامة "، مثلcall وbind وما إلى ذلك. وهي متاحة في النهاية في كلتا الحالتين ، لأن مُنشئ Object المدمج ،Object .__ proto__ = == Function.prototype.

ها هي الصورة:

Image

لذلك ، باختصار ، هناك اختلافان:

class Rabbit class Rabbit extends Object
needs to call super() in constructor
Rabbit.__proto__ === Function.prototype Rabbit.__proto__ === Object
خريطة الدورة التعليمية

التعليقات

إقرأ هذا قبل أن تضع تعليقًا…
  • إذا كان لديك اقتراحات أو تريد تحسينًا - من فضلك من فضلك إفتح موضوعًا فى جيتهاب أو شارك بنفسك بدلًا من التعليقات.
  • إذا لم تستطع أن تفهم شيئّا فى المقال - وضّح ماهو.
  • إذا كنت تريد عرض كود استخدم عنصر <code> ، وللكثير من السطور استخدم <pre>، ولأكثر من 10 سطور استخدم (plnkr, JSBin, codepen…)