ما قصد داریم این پروژهٔ متنباز را در دسترس همهٔ مردم در سرتاسر دنیا قرار دهیم.
به ترجمهٔ محتوای این آموزش به زبان خودتان کمک کنید/a>.
ویژگی و متدهای ایستا
ما میتوانیم یک متد را به تمام کلاس اختصاص دهیم. چنین متدهایی ایستا (static) نامیده میشوند.
در یک کلاس، آنها با کلمه کلیدی static استفاده میشوند، مثل این:
این کار دقیقا مانند این است که به طور مستقیم یک ویژگی را مقداردهی کنیم:
مقدار 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() در اینجا:
حالا هر بار که نیاز داشته باشیم یک خلاصه از امروز بسازیم، میتوانیم Article.createTodays() را فراخوانی کنیم. یکبار دیگر هم میگوییم، این متدی از مقاله (article) نیست بلکه متدی از کل کلاس است.
متدهای ایستا همچنین در کلاسهای مربوط به پایگاه داده (database) برای جستوجو/ذخیره/حذف ورودیها از پایگاه داده هم استفاده میشوند، مثلا اینگونه:
// کلاسی خاص برای مدیریت مقالهها است Article با فرض اینکه
// :id متد ایستا برای حذف مقالهها توسط
Article.remove({id: 12345});
متدهای ایستا بر روی کلاسهای قابل فراخوانی هستند نه بر روی شیءها.
برای مثال، چنین کدی کار نخواهد کرد:
// ...
article.createTodays(); /// Error: article.createTodays is not a function
ویژگیهای ایستا
میتوانیم ویژگیهای ایستا هم داشته باشیم، آنها مانند ویژگیهای معمولی کلاس بنظر میرسند اما قبل از آنها static وجود دارد:
درست مانند مقداردهی مستقیم به 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ارثبری میکند.
به عنوان یک نتیجه، ارثبری هم برای متدهای معمولی کار میکند و هم برای متدهای ایستا.
بفرمایید، بیایید این موضوع را با استفاده از کد بررسی کنیم:
خلاصه
متدهای ایستا برای عملکردی استفاده میشوند که «به صورت کامل» به کلاس تعلق دارد. این موضوع به یک نمونه موجود از کلاس مربوط نمیشود.
برای مثال، متدی برای مقایسه 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 ادامه مییابد.
تمارین
همانطور که میدانیم، تمام شیءهای به صورت طبیعی از Object.prototype ارثبری میکنند و به متدهای «عموnode می» مثل hastOwnProperty و بقیه آنها دسترسی دارند.
برای مثل:
اما اگر ما به طور واضح "class Rabbit extends Object" را بیان کنیم، سپس نتیجه از یک کلاس ساده "class Rabbit" متفاوت خواهد بود؟
تفاوت در چیست؟
اینجا مثالی از چنین کدی داریم (این کد کار نمیکند – چرا؟ آن را درست کنید):
class Rabbit extends Object {
constructor(name) {
this.name = name;
}
}
let rabbit = new Rabbit("Rab");
alert( rabbit.hasOwnProperty('name') ); // ارور
اول، بیایید ببینیم که چرا کد کار نمیکند.
اگر سعی کنیم که آن را اجرا کنیم دلیل واضح میشود. سازنده کلاس ارثبر باید super() را فراخوانی کند. در غیر این صورت "this" «تعریف شده» نخواهد بود.
حل این مشکل:
اما این هنوز تمام ماجرا نیست.
حتی بعد از رفع این مشکل، هنوز هم یک تفاوت اساسی بین "class Rabbit extends Object" و class Rabbit وجود دارد.
همانطور که میدانیم، سینتکس “extends” دو پروتوتایپ را تنظیم میکند:
- بین
"prototype"سازنده تابعها (برای متدها). - بین خود سازنده تابعها (برای متدهای ایستا).
در مورد class Rabbit extends Object یعنی:
پس حالا Rabbit به دسترسی متدهای ایستای Object از طریق Rabbit را فراهم میکند، مثلا اینگونه:
اما اگر ما extends 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 ); // به صورت پیشفرض، مانند هر تابعی
// وجود ندارد Rabbit ارور، چنین تابعی درون
alert ( Rabbit.getOwnPropertyNames({a: 1, b: 2})); // ارور
پس در این صورت Rabbit دسترسی به متدهای ایستای Object را فراهم نمیکند.
راستی، Function.prototype دارای متدهای «عمومی» تابع است، مثل call، bind و غیره. آنها سرانجام در هر دو مورد در دسترس هستند چون سازنده درونساخت Object، Object.__proto__ === Function.prototype را دارد.
اینجا تصویر آن را داریم:
پس، به طور خلاصه، دو تفاوت وجود دارد:
| class Rabbit | class Rabbit extends Object |
|---|---|
| – | باید super() را دورن سازنده فراخوانی کند |
Rabbit.__proto__ === Function.prototype |
Rabbit.__proto__ === Object |
- © 2007—2026 Ilya Kantor
- دربارهٔ پروژه
- تماس با ما
نظرات
<code>استفاده کنید، برای چندین خط – کد را درون تگ<pre>قرار دهید، برای بیش از ده خط کد – از یک جعبهٔ شنی استفاده کنید. (plnkr، jsbin، codepen…)