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

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

كما يمكننا تعيين خاصية لدالة ال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” الموروث.

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

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.

ها هي الصورة:

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

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