«کلاس در جاوا اسکریپت» (Class) مانند بسیاری از زبانهای برنامه نویسی دیگر، یکی از مفاهیم مهم و پایهای در برنامه نویسی شیگرا (OOP) محسوب میشود. در این مقاله آموزشی به این سوال پاسخ داده شده است که «کلاس در جاوا اسکریپت چیست (Class in JavaScript)، چگونه از کلاس در جاوا اسکریپت استفاده میشود (همراه با مثالهای عملی با کدهای جاوا اسکریپت) و همچنین توضیحاتی پیرامون سازنده کلاس و سایر مفاهیم مرتبط با کلاس جاوا اسکریپت ارائه شده است.
استفاده از Class در جاوا اسکریپت
در نسخه اکما اسکریپت 6 (ES6) که در سال 2015 توسعه داده شد، کلاسها به زبان جاوا اسکریپت (JavaScript) افزوده شدند. در جاوا اسکریپت از «ارثبری وابسته به نمونه اولیه» (Prototypal Inheritance) استفاده میشود. یعنی شی در جاوا اسکریپت خصوصیتها و متدهایی را از شی نمونه اولیه خود به ارث میبرد. مفهوم سنتی کلاس یعنی «نقشه ساختی برای شیها» که در زبانهایی مثل جاوا (Java) یا سوئیفت (Swift) به کار گرفته میشد در جاوا اسکریپت وجود ندارد. ارثبری وابسته به نمونه اولیه تنها با اشیا سر و کار دارد.
در ارثبری وابسته به نمونه اولیه امکان شبیهسازی ارثبری سنتی کلاس وجود دارد. برای استفاده از مفهوم سنتی کلاس در جاوا اسکریپت، سینتکس «class» در نسخه استاندارد اکما اسکریپت 6 (ES2015) معرفی شده است. در واقع، سینتکس class در ES2015، یک «تسهیل کننده نحوی» (Syntactic Sugar) برای ارثبری وابسته به نمونه اولیه به حساب میآید. در ادامه این مقاله آموزشی به نحوه تعریف کلاس، مقداردهی اولیه نمونه (Initialize The Instance)، معرفی انواع کلاسهای خصوصی (Private) و عمومی (Public) و روشها و فیلدهای استاتیک (Static Fields) پرداخته خواهد شد.
کلاس در جاوا اسکریپت چیست ؟
کلاس، به طور کلی مانند نقشهای برای ایجاد شی است و شی نمونهای از کلاس محسوب میشود. از طریق کلاسها میتوان اشیائی تعریف کرد که قابلیت توسعه و تغییر داشته باشند. میتوان گفت که به طور کلی کلاسها از خصوصیتها (خصیصه | Property)، «متدها» (Method) و سازندهها (Constructor) تشکیل شدهاند.
خصوصیتها، صفتهای یک کلاس هستند که در کلاس یا شی ذخیره میشوند. متدها مانند توابع برنامه نویسی، رویهای تعریف شده را روی شی اعمال میکنند. در جاوا اسکریپت سنتی، تعریف کلاس از طریق دستور new function() بود، اما امروزه در جاوا اسکریپت مدرن، کلاسها از طریق کلمه کلیدی class تعریف شدهاند که باز هم نوعی تابع محسوب میشوند. در ادامه، قاعده نحوی تعریف یک کلاس در جاوا اسکریپت نشان داده شده است.
class User {
// بدنه کلاس
}
کد فوق کلاسی با نام User را تعریف و علامت { } (آکولاد) نیز بدنه کلاس را مشخص میکند. میتوان برای کلاس نامی تعیین نکرد، بلکه کلاس را به طور مستقیم به یک متغیر اختصاص داد. در ادامه مثالی برای روشنتر شدن این موضوع آمده است:
const UserClass = class {
// بدنه کلاس
};
به راحتی میتوان یک کلاس را در جاوا اسکریپت به عنوان بخشی از ماژول (Module) ES2015 استخراج کرد. در قطعه کد زیر، سینتکس یک استخراج پیش فرض (Default Export) قابل مشاهده است.
export default class User {
// بدنه کلاس در این بخش قرار میگیرد
}
سینتکس export نامگذاری شده نیز به صورت زیر است.
export class User {
// بدنه کلاس در این بخش قرار میگیرد
}
کلاسها برای ایجاد یک نمونه از کلاس به کار میروند. در واقع نمونه کلاس یک شی به حساب میآید که دادهها و رفتارهای آن توسط کلاس تعریف میشوند.
در جاوا اسکریپت، از طریق عملگر new نمونهای از کلاس ساخته میشود. مانند خط زیر، میتوان با استفاده از عملگر new، نمونهای از کلاس User ایجاد کرد.
const myUser = new User();
در مثال فوق، new User() نمونهای از کلاس User میسازد که شامل تمام Propertyها و متدهای کلاس User است. در بخش بعدی در زمینه مفهوم سازنده یا همان Constructor در جاوا اسکریپت توضیحاتی ذکر شده است. برای درک بهتر این مفهوم، باید گفت که فیلد در جاوا اسکریپت به متغیرهایی گفته میشود که در داخل کلاس تعریف شدهاند.
مقداردهی اولیه نمونه کلاس در جاوا اسکریپت با Constructor
حال پس از پاسخ به این سوال که کلاس در جاوا اسکریپت چیست در این بخش، متد سازنده (Constructor) به عنوان یکی از مفاهیم مهم شیگرایی در جاوا اسکریپت شرح داده شده است. به محض ایجاد یک شی، متد Constructor برای کلاس ایجاد میشود.
متد سازنده، متدی خاص است که به صورت «constructor(param1, param2, ….)» در بدنه کلاس قرار میگیرد و نمونه را مقداردهی اولیه میکند. در این متد، امکان مقداردهی اولیه برای فیلدها یا هر نوع تنظیماتی روی شی وجود دارد. در مثال زیر، Constructor مقدار اولیه فیلد name را تعیین میکند.
class User {
constructor(name) {
this.name = name;
}
}
سازنده User با استفاده از پارامتر name، فیلد this.name را مقداردهی اولیه میکند. مقدار داخل سازنده this برابر با نمونهای خواهد بود که تازه ایجاد شده است. آرگومانهای مورد استفاده برای ساخت نمونه کلاس به پارامترهای Constructor تبدیل میشوند:
class User {
constructor(name) {
name; // => 'Jon Snow'
this.name = name;
}
}
const user = new User('Jon Snow');
همانطور که در کدهای فوق ملاحظه میشود، پارامتر name در داخل Constructor، مقدار Jon Snow را دارد. اگر برای کلاسی، Constructor تعریف نشود، یک Constructor پیش فرض ایجاد میشود. Constructor پیش فرض تابعی خالی است که نمونه را تغییر نمیدهد. همچنین نیاز به ذکر است که هر کلاس جاوا اسکریپت حداکثر میتواند دارای یک Constructor باشد. پس از آشنایی با مفهوم کلاس در جاوا اسکریپت و متد سازنده برای کلاس در بخش زیر به مفهوم فیلدها و انواع آنها در جاوا اسکریپت پرداخته شده است.
انواع فیلدهای کلاس در جاوا اسکریپت
فیلدهای کلاس متغیرهایی هستند که اطلاعات را در خود نگه میدارند. فیلدها را میتوان به 2 بخش تقسیم کرد:
- فیلدهای نمونه کلاس
- فیلدهای خود کلاس (معروف به فیلد استاتیک)
همچنین به طور کلی 2 سطح دسترسی برای فیلدها در نظر گرفته میشود:
- سطح دسترسی عمومی (Public): این سطح دسترسی برای مواقعی مورد استفاده قرار میگیرد که نیاز باشد خارج از کلاس نیز به یک فیلد دسترسی داشت. در این سطح از دسترسی، فیلد در هر جایی قابل دسترسی است.
- سطح دسترسی خصوصی (Private): در این سطح نیز، فیلد فقط در بدنه کلاس قابل دسترسی است.
فیلدهای نمونه عمومی چیست ؟
برای درک بهتر مفهوم فیلدهای نمونه عمومی، توضیحاتی روی مثال قبلی ارائه میشود.
class User {
constructor(name) {
this.name = name;
}
}
عبارت this.name = name یک فیلد نمونه را با نام name ایجاد میکند و یک مقدار اولیه به آن اختصاص میدهد. سپس میتوان با استفاده از دسترسی خصوصیت به فیلد name دسترسی داشت.
const user = new User('Jon Snow');
user.name; // => 'Jon Snow'
name یک فیلد عمومی محسوب میشود، چون میتوان خارج از بدنه کلاس User هم به آن دسترسی داشت. مانند سناریوی قبلی، هنگامی که فیلدها به طور غیر مستقیم در Constructor ایجاد میشوند، به دست آوردن فهرست فیلدها ممکن است سخت باشد. چرا که آنها باید از کد سازنده رمزگشایی شوند.
روش بهتر این است که فیلدهای کلاس به طور صریح معرفی شوند. به طور معمول اهمیتی ندارد که Constructor چه کاری را انجام میدهد، زیرا نمونه همواره مجموعه فیلدهای یکسانی دارد. «طرح پیشنهادی فیلدهای کلاس» [+]، امکان تعریف فیلدها را داخل بدنه کلاس فراهم میکند. علاوه بر این میتوان در همان ابتدا مقدار اولیه را مشخص کرد.
class SomeClass {
field1;
field2 = 'Initial value';
// ...
}
در کد زیر، کلاس User ویرایش شده و برای آن فیلد name به عنوان یک فیلد Public تعریف شده است.
class User {
name;
constructor(name) {
this.name = name;
}
}
const user = new User('Jon Snow');
user.name; // => 'Jon Snow'
کد name; در بدنه کلاس، name را به عنوان یک فیلد عمومی معرفی میکند. فیلدهای عمومی که به این شکل تعریف میشوند، واضح و گویا هستند. یعنی یک نگاه سریع به تعاریف فیلدها برای درک ساختار کلاس کافی است. علاوه بر این، میتوان فیلد کلاس را در هنگام تعریف مقداردهی اولیه کرد.
class User {
name = 'Unknown';
constructor() {
// بدون مقداردهی اولیه
}
}
const user = new User();
user.name; // => 'Unknown'
عبارت ‘name = ‘Unknown در بدنه کلاس، فیلد name را تعریف کرده و آن را با مقدار ‘Unknown’ مقداردهی اولیه میکند. همچنین نیاز به ذکر است که هیچ محدودیتی برای دسترسی یا بهروزرسانی فیلدهای عمومی وجود ندارد و میتوان مقادیری را به فیلدهای عمومی در Constructor، متدها و در خارج از کلاس اختصاص داد.
همچنین باید گفت چنانچه در جاوا اسکریپت، برای فیلدی سطح دسترسی تعیین نشده باشد، سطح دسترسی آن به صورت پیشفرض Public در نظر گرفته میشود.
فیلدهای نمونه خصوصی چیست ؟
همانطور که گفته شد، هنگامی که یک کلاس ایجاد میشود، تعدادی فیلد هم برای ذخیره مقادیر اشیاء تعریف میشوند که بسته به نیاز، برای متدها یا به عنوان یک فیلد موقت به کار میروند. دسترسی کاربر به تمام فیلدها بسیار خطرناک است و ممکن است کاربر تغییراتی ایجاد کند که در روند کار متدها اختلال ایجاد شود. برای جلوگیری از این مهم، از روش کپسولهسازی (Encapsulation) استفاده میشود.
به عبارتی دیگر، کپسولهسازی مفهوم مهمی است که امکان مخفیسازی جزئیات داخلی یک کلاس را فراهم میکند. استفاده از یک کلاس کپسولهسازی شده فقط به رابط کاربری عمومی آن بستگی دارد که کلاسها را آماده میکند و به جزئیات پیادهسازی کلاس ارتباطی ندارد. هنگامی که به اعمال تغییر در جزئیات پیادهسازی نیاز باشد، کلاسهایی که با در نظر گرفتن کپسولهسازی تشکیل شدهاند، راحت تر بهروزرسانی میشوند.
یک روش خوب برای مخفی کردن دادههای داخلی یک شی، استفاده از فیلدهای نمونه خصوصی (Private Instance Field) است. این فیلدها فقط در کلاسی که به آن تعلق دارند، قابل خواندن و تغییر هستند. عناصر خارج از کلاس، نمیتوانند فیلدهای خصوصی را مستقیماً تغییر دهند. به عبارتی دیگر، فیلدهای خصوصی فقط در بدنه کلاس قابل دسترسی هستند.
برای اینکه یک فیلد به فیلد خصوصی تغییر یابد، باید نام فیلد به همراه نماد اختصاصی هشتگ (#) به عنوان پیشوند آن قرار داده شود. برای مثال، #myFieldیک فیلد خصوصی محسوب میشود و برای تعریف، فراخوانی یا اعمال تغییرات در فیلد، باید پیشوند # نیز ذکر شود. در قطعه کد زیر نحوه مقداردهی اولیه #nameنشان داده شده است.
class User {
#name;
constructor(name) {
this.#name = name;
}
getName() {
return this.#name;
}
}
const user = new User('Jon Snow');
user.getName(); // => 'Jon Snow'
user.#name; // SyntaxError is thrown
#name یک فیلد خصوصی است که میتوان در بدنه کلاس User به آن دسترسی داشته و آن را تغییر داد. متد getName() هم میتواند به فیلد خصوصی #nameدسترسی داشته باشد. اما اگر سعی در دسترسی به فیلد خصوصی #name از خارج بدنه کلاس User وجود داشته باشد، خطای نحوی به صورت زیر صادر میشود:
SyntaxError: Private field '#name' must be declared in an enclosing class.
این خطای دستوری به معنای این است که فیلد خصوصی #name باید در یک کلاس محصور (Enclosing) تعریف شود.
فیلد استاتیک عمومی در جاوا اسکریپت چیست ؟
همچنین میتوان فیلدهایی را در خود کلاس تعریف کرد که به آنها فیلدهای استاتیک یا «فیلدهای ثابت» گفته میشود. این نوع فیلدها برای تعریف ثابتهای کلاس یا ذخیره اطلاعات خاص برای کلاس مفید هستند.
برای ایجاد فیلدهای استاتیک در یک کلاس جاوا اسکریپت ، باید از کلمه کلیدی ویژه static به همراه نام فیلد به عنوان مثال به صورت static myStaticField استفاده شود.
در قطعه کد زیر، نوعی فیلد جدید دیگر به نام Type هم اضافه شده است که نوع کاربر (یعنی نوع کاربر ادمین یا کاربر معمولی) را نشان میدهد. فیلدهای استاتیک TYPE_ADMIN و TYPE_REGULAR ثابتهای مفیدی برای نشان دادن تمایز انواع کاربر هستند.
class User {
static TYPE_ADMIN = 'admin';
static TYPE_REGULAR = 'regular';
name;
type;
constructor(name, type) {
this.name = name;
this.type = type;
}
}
const admin = new User('Site Admin', User.TYPE_ADMIN);
admin.type === User.TYPE_ADMIN; // => true
در قطعه کد فوق، static TYPE_ADMIN و static TYPE_REGULAR متغیرهای استاتیک را در کلاس User تعریف میکنند. برای دسترسی به فیلدهای استاتیک باید نام کلاس پس از نام فیلد قرار بگیرد. یعنی به صورت User.TYPE_ADMIN و User.TYPE_REGULAR باشد. موارد مطرح شده برای آشنایی با فیلد استاتیک عمومی بودند؛ در ادامه به مبحث فیلد استاتیک خصوصی (Private Static Field) پرداخته خواهد شد.
فیلد استاتیک خصوصی چیست ؟
گاهی حتی فیلدهای استاتیک هم جزئیاتی از پیادهسازی محسوب میشوند که تمایل به مخفی کردن آنها وجود دارد. در این رابطه میتوان فیلدهای استاتیک را خصوصی کرد. برای این کار باید طبق مرحله مطرح شده در ادامه این بخش، عمل شود.
برای خصوصی کردن فیلد استاتیک، پیشوند نام فیلد با نماد ویژه # مانند static #myPrivateStaticField نوشته میشود. به عنوان مثال، این کار زمانی انجام میشود که قصد محدود کردن تعداد نمونههای کلاس User وجود دارد. برای مخفی کردن جزئیات در مورد محدودیتهای نمونه، میتوان به صورت زیر فیلدهای استاتیک خصوصی ایجاد کرد:
class User {
static #MAX_INSTANCES = 2;
static #instances = 0;
name;
constructor(name) {
User.#instances++;
if (User.#instances > User.#MAX_INSTANCES) {
throw new Error('Unable to create User instance');
}
this.name = name;
}
}
new User('Jon Snow');
new User('Arya Stark');
new User('Sansa Stark'); // throws Error
فیلدهای استاتیک User.#MAX_INSTANCES حداکثر تعداد نمونههای مجاز را تنظیم میکنند، در حالی که فیلد استاتیک User.#instances تعداد واقعی نمونهها را میشمارد.
از مزیتهای کپسولهسازی این است که فیلدهای استاتیک خصوصی فقط در کلاس User قابل دسترسی هستند و هیچ چیزی از دنیای بیرونی نمیتواند در مکانیسم این محدودیتها تاثیر بگذارد.
متدها در کلاس جاوا اسکریپت
وظیفه فیلدها نگهداری دادهها است. اما تغییر دادهها از طریق توابع خاصی انجام میشود که بخشی از کلاسها محسوب میشوند و به آنها متد میگویند. کلاسهای جاوا اسکریپت از هر دو روش متدهای نمونه و متدهای استاتیک پشتیبانی میکنند.
متدهای نمونه چگونه کار می کنند ؟
متدهای نمونه (Instance Methods) قابلیت دسترسی به دادههای نمونه و تغییر آنها را دارند. متدهای نمونه میتوانند متدهای نمونه دیگر و همچنین هر متد ایستایی را فراخوانی کنند. به عنوان مثال، در قطعه کد زیر یک متد ()getName تعریف شده که نام کلاس User را برمیگرداند.
class User {
name = 'Unknown';
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const user = new User('Jon Snow');
user.getName(); // => 'Jon Snow'
در کد بالا، getName() { … } متدی در کلاس User به حساب میآید. user.getName()، فراخوانی متد است که متد را اجرا میکند و در صورت وجود مقدار محاسبه شده، آن مقدار را برمیگرداند.
در متد کلاس و همچنین در Constructor، مقدار this همان نمونه کلاس است. از عبارت this به صورت this.field برای دسترسی به دادههای نمونه استفاده میشود. حتی میتوان عبارت this.method() را برای فراخوانی متدهای دیگر به کار برد. در قطعه کد زیر برای آشنایی بیشتر با این مفهوم، متد جدید nameContains(str) اضافه شده که یک پارامتر دارد و متد دیگری را فراخوانی میکند.
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
nameContains(str) {
return this.getName().includes(str);
}
}
const user = new User('Jon Snow');
user.nameContains('Jon'); // => true
user.nameContains('Stark'); // => false
در کد فوق، nameContains(str) { … } متدی از کلاس User است که پارامتری به نام str دارد. بیشتر از آن، متد دیگری از نمونه this.getName() را برای دریافت نام user اجرا میکند.
مانند فیلدها، یک متد هم میتواند خصوصی باشد. برای خصوصیسازی متدها نیاز است که نام آنها به همراه پیشوند # نوشته شود. در مثال زیر متد getName() خصوصی شده است.
class User {
#name;
constructor(name) {
this.#name = name;
}
#getName() {
return this.#name;
}
nameContains(str) {
return this.#getName().includes(str);
}
}
const user = new User('Jon Snow');
user.nameContains('Jon'); // => true
user.nameContains('Stark'); // => false
user.#getName(); // SyntaxError is thrown
اکنون #getName() متدی خصوصی محسوب میشود. در متد nameContains(str)، متد خصوصی از طریق this.#getName() فراخوانی میشود. همانطور که گفته شد، به دلیل اینکه #getName() متدی خصوصی است، نمیتوان آن را از خارج از بدنه کلاس User فراخوانی کرد.
انواع Property اشیا در جاوا اسکریپت
در جاوا اسکریپت دو نوع خصوصیت برای اشیاء تعریف میشود:
- خصوصیتهای دادهای (Data Properties)
- خصوصیتهای گیرنده و تنظیمکننده
خصوصیتهای دادهای
یک خصوصیت دادهای شامل یک موقعیت مکانی برای یک مقدار دادهای است. مثالی از خصوصیتهای دادهای در ادامه آورده شده است.
let person = {
// خصوصیتهای دادهای
firstName: ‘John’,
};
Getter و Setter در جاوا اسکریپت چیست ؟
گیرنده (Getter) و تنظیمکننده (Setter) شبیه فیلد معمولی هستند، اما کنترل بیشتری روی نحوه دسترسی و تغییرات فیلد دارند. Getter برای به دست آوردن مقدار فیلد اجرا میشود در حالی که هدف از Setter، تنظیم یک مقدار است.
برای اطمینان از اینکه خاصیت name کلاس User خالی نمیماند، در مثال زیر فیلد خصوصی #nameValueدر Getter و Setter پر شدهاند.
class User {
#nameValue;
constructor(name) {
this.name = name;
}
get name() {
return this.#nameValue;
}
set name(name) {
if (name === '') {
throw new Error(`name field of User cannot be empty`);
}
this.#nameValue = name;
}
}
const user = new User('Jon Snow');
user.name; // The getter is invoked, => 'Jon Snow'
user.name = 'Jon White'; // The setter is invoked
user.name = ''; // The setter throws an Error
عبارت گیرنده get name() {…} زمانی اجرا میشود که به مقدار فیلد user.name دسترسی وجود داشته باشد. در حالی که set name(name) {…} زمانی اجرا میشود که فیلد user.name = ‘Jon White’ آپدیت شده باشد. اگر یک رشته مقدار جدیدی نداشته باشد (خالی باشد)، Setter خطا صادر میکند.
متدهای ایستای کلاس در جاوا اسکریپت
متدهای استاتیک یا متدهای ایستا (Static Methods) توابعی هستند که مستقیماً به کلاس اضافه میشوند. آنها به جای منطق نمونهای از کلاس (شی)، منطق مربوط به کلاس را نگهداری میکنند. برای ایجاد یک متد استاتیک، از کلمه کلیدی static و پس از آن ساختار دستوری متد معمولی به کار برده میشود. به عنوان مثال میتوان static myStaticMethod() { … } را به عنوان یک متد استاتیک نوشت.
دو قاعده کلی برای متدهای استاتیک وجود دارد که به شرح زیر هستند:
- متد استاتیک میتواند به فیلدهای استاتیک دسترسی داشته باشد.
- متد استاتیک نمیتواند به فیلدهای نمونه دسترسی پیدا کند.
به عنوان مثالی برای درک بهتر این موضوع، در بخش زیر متد استاتیکی ایجاد شده که تشخیص میدهد که آیا کاربری با نامی خاص، قبلاً ثبتنام شده است یا خیر؟
class User {
static #takenNames = [];
static isNameTaken(name) {
return User.#takenNames.includes(name);
}
name = 'Unknown';
constructor(name) {
this.name = name;
User.#takenNames.push(name);
}
}
const user = new User('Jon Snow');
User.isNameTaken('Jon Snow'); // => true
User.isNameTaken('Arya Stark'); // => false
isNameTaken() متدی استاتیک است که برای بررسی نامهای پذیرفته شده، از فیلد خصوصی استاتیک User.#takenNames استفاده میکند. متدهای استاتیک میتوانند خصوصی باشند که به صورت static #staticFunction() {…} در میآیند. البته باز هم از قواعد حریم خصوصی پیروی میکنند. یعنی فقط میتوان یک متد استاتیک خصوصی را در بدنه کلاس فراخوانی کرد.
تا این بخش از مقاله آموزشی کلاس در جاوا اسکریپت به مباحثی مانند استفاده از Class در جاوا اسکریپت ، کلاس در جاوا اسکریپت چیست ، Constructor چیست، انواع فیلدهای کلاس در جاوا اسکریپت ، تعریف متدها در کلاس جاوا اسکریپت، انواع Property اشیا در جاوا اسکریپت و متدهای ایستا پرداخته شد. حال در بخش بعدی از این مطلب آموزشی به مبحث «ارثبری» یا وراثت (Inheritance) در کلاس پرداخته میشود.
ارثبری کلاس در جاوا اسکریپت چگونه است؟
مفهوم ارثبری در جاوا اسکریپت به کلاس این امکان را میدهد که خصوصیت یا متدهایی را از کلاس دیگری به ارث ببرد. کلاسها در جاوا اسکریپت با استفاده از کلمه کلیدی «extends» به معنی تعمیم دادن، از ارثبری استفاده میکنند. استفاده از کلمه extends به کلاس فرزند این امکان را میدهد که دارای تمام خصوصیتهای کلاس والد خود شود.
در عبارت class Child extends Parent { }، کلاس Child از کلاس والد خود (Parent)، سازنده، فیلدها و متدها را به ارث میبرد. در ادامه برای مثال، یک کلاس فرزند جدید با نام ContentWriter ایجاد میشود که دارای تمام خصوصیتهای کلاس والد خود (User) است.
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
}
const writer = new ContentWriter('John Smith');
writer.name; // => 'John Smith'
writer.getName(); // => 'John Smith'
writer.posts; // => []
کلاس ContentWriter، سازنده، متد getName() و فیلد name را از کلاس User به ارث میبرد. همچنین، کلاس ContentWriter فیلد جدید posts را تعریف میکند. لازم به ذکر است که کلاس فرزند نمیتواند اعضای خصوصی کلاس والد را به ارث ببرد. در ادامه این بخش از مقاله آموزشی کلاس در جاوا اسکریپت به مباحث نحوه فراخوانی سازنده والد با تابع super() در constructor() و میانبر super در متدها برای ارثبری از نمونه والد پرداخته شده است.
نحوه فراخوانی سازنده والد با تابع super() در constructor()
برای فراخوانی سازنده والد در کلاس فرزند، باید از تابع اختصاصی ()super استفاده شود که در سازنده فرزند وجود دارد.
در مثال زیر سازنده ContentWriter، سازنده والد User را فراخوانی میکند، همچنین فیلد posts نیز مقداردهی اولیه میشود.
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
constructor(name, posts) {
super(name);
this.posts = posts;
}
}
const writer = new ContentWriter('John Smith', ['Why I like JS']);
writer.name; // => 'John Smith'
writer.posts // => ['Why I like JS']
super(name) در کلاس فرزند ContentWriter سازنده کلاس والد User را اجرا میکند. نکته حائز اهمیت این است که در سازنده فرزند، پیش از استفاده از کلمه کلیدی this باید تابع ()super اجرا شود. با استفاده از super() اطمینان حاصل میشود که سازنده والد، نمونه را مقداردهی اولیه کرده است.
class Child extends Parent {
constructor(value1, value2) {
// Does not work!
this.prop2 = value2;
super(value1);
}
}
میانبر super در متدها برای ارثبری از نمونه والد
برای دسترسی به متد والد در یک متد فرزند، باید از میانبر super استفاده شود. در ادامه مثالی برای میانبر super ارائه شده است:
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
constructor(name, posts) {
super(name);
this.posts = posts;
}
getName() {
const name = super.getName();
if (name === '') {
return 'Unknwon';
}
return name;
}
}
const writer = new ContentWriter('', ['Why I like JS']);
writer.getName(); // => 'Unknwon'
متد getName() که در کلاس فرزند ContentWriter وجود دارد، از کلاس والد User مستقیماً به متد super.getName() دسترسی دارد. به عبارتی دیگر، موقع فراخوانی یک متد، اولویت فراخوانی با متد کلاس فرزند است و در صورت عدم وجود متد فرزند، متد کلاس والد فراخوانی میشود. حال اگر همان متدی که در کلاس والد وجود دارد، مجدداً در کلاس فرزند هم تعریف شود، عمل «باز نویسی» (Override) انجام شده است. در Override هم اولویت با متد کلاس فرزند است و متد تعریف شده در کلاس والد نادیده گرفته میشود. برای دسترسی به متدهای استاتیک والد نیز باید از میانبر super با متدهای استاتیک استفاده شود.
در این بخش به مبحث ارثبری کلاس در جاوا اسکریپت با دو روش فراخوانی سازنده والد با تابع super() در constructor() و میانبر super در متدها برای ارثبری از نمونه والد پرداخته شد. در ادامه به بررسی نوع اشیا از طریق عملگر instanceof پرداخته شده است.
بررسی نوع شی با instanceof در کلاس جاوا اسکریپت
«object instanceof Class» عملگری است که تعیین میکند object نمونهای از کلاس است یا خیر. نمونهای از کاربرد عملگر instanceof در ادامه آمده است:
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const user = new User('Jon Snow');
const obj = {};
user instanceof User; // => true
obj instanceof User; // => false
در قطعه کد فوق، user نمونهای از کلاس User است، user instanceof User ارزیابی true را نتیجه میدهد. شی خالی {} نمونهای از کلاس User نیست، به طور مشابه obj instanceof User هم false است. instanceof هم یک چند شکلی محسوب میشود که عملگر فرزند را به عنوان نمونهای از کلاس والد تشخیص میدهد.
class User {
name;
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ContentWriter extends User {
posts = [];
constructor(name, posts) {
super(name);
this.posts = posts;
}
}
const writer = new ContentWriter('John Smith', ['Why I like JS']);
writer instanceof ContentWriter; // => true
writer instanceof User; // => true
در کد بالا، writer نمونهای از کلاس فرزند ContentWriter است. عملگر writer instanceof ContentWriter برابر با true است. در عین حال، ContentWriter یک کلاس فرزند از کلاس User به حساب میآید. بنابراین writer instanceof Use هم برابر با true خواهد بود.
سوالی که در این بخش وجود دارد این است که چگونه میتوان کلاس نمونه دقیق را تعیین کرد؟ پاسخ مناسب این است که میتوان از خاصیت Constructor استفاده و آن را مستقیماً با کلاس مقایسه کرد.
writer.constructor === ContentWriter; // => true
writer.constructor === User; // => false
- در صورتی تمایل به یادگیری بخش وراثت در جاوا اسکریپت به بیان دیگر، مشاهده مقاله آموزش وراثت در جاوا اسکریپت — راهنمای کاربردی پیشنهاد میشود.
کلاسها و نمونههای اولیه در جاوا اسکریپت
تا این بخش از آموزش به مباحث استفاده از Class در جاوا اسکریپت، کلاس در جاوا اسکریپت چیست ، Constructor چیست، انواع فیلدهای کلاس در جاوا اسکریپت و مباحث دیگر پرداخته شد. در این بخش مفهوم ارثبری نمونه اولیه کلاس در قالب دو روش ارائه شده است.
ساختار دستوری کلاس در جاوا اسکریپت، رویکرد بسیار خوبی برای انتزاع (تجرید) از ارثبری نمونه اولیه دارد. در مثالهای قبلی مطرح شده، برای تعریف ساختار دستوری class از عبارت prototype استفاده نشده بود. اما کلاسها با در نظر گرفتن ارثبری نمونه اولیه ساخته شدهاند. هر کلاس یک تابع محسوب میشود و زمانی که به عنوان Constructor فراخوانی میشود، یک نمونه ایجاد میکند. دو قطعه کد زیر معادل هستند و در ادامه مدل ساختار دستوری کلاس مطرح شده است:
class User {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
const user = new User('John');
user.getName(); // => 'John Snow'
user instanceof User; // => true
در کد زیر نیز مدل ساختار دستوری با استفاده از نمونه اولیه ذکر شده است.
function User(name) {
this.name = name;
}
User.prototype.getName = function() {
return this.name;
}
const user = new User('John');
user.getName(); // => 'John Snow'
user instanceof User; // => true
برای افرادی که با مکانیزم ارثبری کلاس در زبانهای برنامه نویسی جاوا و سوئیفت آشنا هستند، ساختار دستوری کلاس روش راحتتری است. به طور کلی، پیشنیاز استفاده از ساختار دستوری کلاس در جاوا اسکریپت، رسیدن به درک خوب در مبحث ارثبری نمونه اولیه است.
قابلیت دسترسی ویژگیهای کلاس
ویژگیهای کلاس مطرح شده در این بخش در ES2015 منتشر شده و همچنین در قسمت Fieldهای همین مقاله آموزشی ارائه شدهاند.
در اواخر سال ۲۰۱۹، ویژگیهای کلاس به بخشهای زیر تقسیم شدند:
- فیلدهای نمونه عمومی و خصوصی بخشی از طرح فیلدهای کلاس (Class Fields Proposal) هستند.
- متدهای نمونه خصوصی و روشهای دسترسی بخشی از طرح متدهای خصوصی کلاس (Class Private Methods Proposal) هستند.
- فیلدهای استاتیک عمومی و خصوصی و متدهای استاتیک خصوصی بخشی از طرح ویژگیهای استاتیک کلاس (Class Static Features Proposal) به حساب میآیند.
- بقیه بخشی از استاندارد ES2015 هستند.
به این ترتیب آموزش کلاس در جاوا اسکریپت در این مقاله به اتمام میرسد. در بخش پایانی این مطلب، فیلمهای آموزشی کاربردی جاوا اسکریپت برای یادگیری بیشتر علاقهمندان معرفی شدهاند.
جمعبندی
در این مقاله به مبحث کلاس در جاوا اسکریپت و مفاهیم مرتبط با آن به همراه مثالهای سادهای پرداخته شد تا یادگیری آن برای افراد مبتدی و علاقهمند به یادگیری زبان برنامه نویسی جاوا اسکریپت نیز مناسب باشد. کلاسهای جاوا اسکریپت از طریق سازندهها، نمونهها را مقداردهی اولیه کرده و همچنین فیلدها و متدها را تعریف میکنند. علاوه بر آن، حتی میتوان فیلدها و متدها را با استفاده از کلمه کلیدی static به خود کلاس اضافه کرد.
وراثت هم از طریق کلمه کلیدی extends انجام میشود که با استفاده از آن به راحتی میتوان از کلاس والد یک کلاس فرزند ایجاد کرد. کلمه کلیدی super هم برای دسترسی به کلاس والد از کلاس فرزند به کار میرود. جهت بهرهبرداری از مزیتهای کپسولهسازی هم نیاز است که فیلدها و متدها برای مخفی کردن جزئیات داخلی کلاسها، خصوصیسازی شوند. برای خصوصیسازی فیلدها و متدها نام آنها باید با # شروع شود.