ما میتوانیم یک متد را به تمام کلاس اختصاص دهیم. چنین متدهایی ایستا (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 در «بالای» مقالهها (articles) قرار دارد، به عنوان روشی برای مقایسه آنها. این متدی برای یک مقاله(article) نیست، بلکه برای کل کلاس است.
مثال دیگر متدی به نام “factory” (به معنی تولیدکننده) است.
فرض کنید، ما به چند راه برای ایجاد یک مقاله نیاز داریم:
- ساختن از طریق پارامترها (
title،dateو غیره). - ساختن یک مقاله خالی با تاریخ امروز.
- …یا به روشی دیگر.
اولین راه میتواند با استفاده از تابع سازنده پیادهسازی شود. و برای راه دوم میتوانیم یک متد ایستا برای کلاس بسازیم.
مانند Article.createTodays() در اینجا:
class Article {
constructor(title, date) {
this.title = title;
this.date = date;
}
static createTodays() {
// this = Article ،به یاد داشته باشید
return new this("خلاصهی امروز", new Date());
}
}
let article = Article.createTodays();
alert( article.title ); // خلاصهی امروز
حالا هر بار که نیاز داشته باشیم یک خلاصه از امروز بسازیم، میتوانیم Article.createTodays() را فراخوانی کنیم. یکبار دیگر هم میگوییم، این متدی از مقاله (article) نیست بلکه متدی از کل کلاس است.
متدهای ایستا همچنین در کلاسهای مربوط به پایگاه داده (database) برای جستوجو/ذخیره/حذف ورودیها از پایگاه داده هم استفاده میشوند، مثلا اینگونه:
// کلاسی خاص برای مدیریت مقالهها است Article با فرض اینکه
// :id متد ایستا برای حذف مقالهها توسط
Article.remove({id: 12345});
متدهای ایستا بر روی کلاسهای قابل فراخوانی هستند نه بر روی شیءها.
برای مثال، چنین کدی کار نخواهد کرد:
// ...
article.createTodays(); /// Error: article.createTodays is not a function
ویژگیهای ایستا
میتوانیم ویژگیهای ایستا هم داشته باشیم، آنها مانند ویژگیهای معمولی کلاس بنظر میرسند اما قبل از آنها 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 = "زمین";
constructor(name, speed) {
this.speed = speed;
this.name = name;
}
run(speed = 0) {
this.speed += speed;
alert(`${this.name} با سرعت ${this.speed} میدود.`);
}
static compare(animalA, animalB) {
return animalA.speed - animalB.speed;
}
}
// Animal ارثبری از
class Rabbit extends Animal {
hide() {
alert(`${this.name} قایم میشود!`);
}
}
let rabbits = [
new Rabbit("خرگوش سفید", 10),
new Rabbit("خرگوش مشکی", 5)
];
rabbits.sort(Rabbit.compare);
rabbits[0].run(); // خرگوش مشکی با سرعت 5 میدود
alert(Rabbit.planet); // زمین
حالا زمانی که Rabbit.compare را فراخوانی میکنیم، Animal.compare که به ارث برده شده فراخوانی خواهد شد.
این چگونه کار میکند؟ دوباره، با استفاده از پروتوتایپها. همانطور که ممکن است از قبل حدس زده باشید، extends به کلاس Rabbit ویژگی [[Prototype]] میدهد که به Animal رجوع میکند.
پس Rabbit extends Animal دو رجوع [[Prototype]] میسازد:
- تابع
Rabbitبه صورت پروتوتایپی از تابعAnimalارثبری میکند. - ویژگی
Rabbit.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() {
...
}
}
از لحاظ فنی، تعریف کردن به صورت ایستا درست مانند مقداردهی به خود کلاس است:
MyClass.property = ...
MyClass.method = ...
ویژگیها و متدهای ایستا به ارث برده میشوند.
برای class B extends A پروتوتایپ کلاس B خودش به A اشاره میکند: B.[[Prototype]] = A. پس اگر یک فیلد درون B پیدا نشد، جستوجو درون A ادامه مییابد.
نظرات
<code>استفاده کنید، برای چندین خط – کد را درون تگ<pre>قرار دهید، برای بیش از ده خط کد – از یک جعبهٔ شنی استفاده کنید. (plnkr، jsbin، codepen…)