کلاس ها در ES6 (قسمت پایانی)

25 آبان 1398
کلاس ها در ES6

جاوا اسکریپت OOP: کلاس ها در ES6

حالا که با مباحث اصلی شیء گرایی ES5 آشنا شدیم باید به سراغ ES6 برویم. کلاس ها در ES6 معرفی شدند و در حال حاضر در تمام مرورگرهای به روز (کروم، فایرفاکس، سافاری و...) پشتیبانی می شوند. بنابراین می توانیم با خیال راحت از آن ها استفاده کنیم. اگر قبلا از زبان های برنامه نویسی شیء گرا مانند جاوا یا #C یا PHP و ... استفاده کرده باشید، کار با کلاس ها در ES6 بسیار ساده خواهد بود.

من یک کلاس بسیار ساده برایتان می نویسم:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

دقیقا مانند دیگر زبان های شیء گرا، یک کلاس داریم که می توانیم درون آن constructor و خصوصیات و متدهای مختلفی داشته باشیم. کد بالا یک کلاس به نام Person است که یک constructor دارد. کار این constructor ساخت دو خصوصیت برای شیء است: نام و نام خانوادگی. حالا برای تست کردن آن یک نمونه (instance) از این کلاس را می سازیم:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

const mary = new Person('Mary', 'Williams');

ساختن یک نمونه از یک کلاس به همان روش قبل انجام شده و تغییری نکرده است. برای اینکه شیء mary را مشاهده کنیم، آن را در مرورگر console.log می کنیم:

const mary = new Person('Mary', 'Williams');
console.log(mary);
شیء Mary
شیء Mary

در تصویر بالا مشاهده می کنید که شیء __proto__ هنوز موجود است، حتی با اینکه از کلاس ها استفاده کرده ایم! باید بدانید که کلاس های جاوا اسکریپت هیچ تفاوتی با استفاده از روش های ES5 ندارند و در پشت صحنه (در موتور جاوا اسکریپت) همه چیز به همان حالت ES5 تبدیل می شود. بنابراین اضافه شدن این کلاس ها فقط برای راحت تر شدن من و شما بوده است و از نظر سرعت و عملکرد تقریبا هیچ تفاوتی با حالت قبل ندارد. به طور مثال من یک متد به نام greeting را به این کلاس اضافه می کنم:

class Person {
    constructor(firstName, lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    greeting () {
        return `Hello there ${this.firstName} ${this.lastName}`
    }
}

سپس به جای mary، این متد را برای mary خروجی می گیریم:

console.log(mary.greeting());

نتیجه ی این کد در کنسول مرورگر به صورت Hello there Mary Williams دیده می شود. حالا باید متد محاسبه ی سن را تولید کنیم. برای این کار به یک dob (تاریخ تولد) نیاز داریم تا بر اساس آن سن کاربر را محاسبه کنیم:

class Person {
    constructor(firstName, lastName, dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthday = new Date(dob);
    }

    greeting() {
        return `Hello there ${this.firstName} ${this.lastName}`
    }
}

const mary = new Person('Mary', 'Williams', '11-13-1980');
console.log(mary);

ابتدا تاریخ تولد را به صورت پارامتر به constructor داده ایم تا به صورت یک خصوصیت برای شیء تعریف شود. سپس mary را ساخته و console.log می کنیم تا مطمئن شویم کد ما بدون اشکال است. نتیجه ی این کد درون مرورگر شما باید شیء mary باشد که تاریخ تولد هم یکی از خصوصیات آن باشد. اگر در رابطه با شیء Date چیزی نمی دانید به مقاله ی قبل مراجعه کنید.

حالا باید متدی برای محاسبه ی سن کاربر را ارائه دهیم:

class Person {
    constructor(firstName, lastName, dob) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.birthday = new Date(dob);
    }

    greeting() {
        return `Hello there ${this.firstName} ${this.lastName}`
    }

    calculateAge() {
        const diff = Date.now() - this.birthday.getTime();
        const ageDate = new Date(diff);
        return Math.abs(ageDate.getUTCFullYear() - 1970);
    }
}

const mary = new Person('Mary', 'Williams', '11-13-1980');
console.log(mary.calculateAge());

قبلا این روش برای محاسبه ی سن کاربر را توضیح داده بودم. ثابت diff تفاوت بین زمان حال (date.now) و زمان تولد (birthday.getTime) را در خود نگه می دارد. ثابت ageDate نیز یک شیء Date (تاریخ) از این تفاوت می سازد چرا که در حال حاضر getTime را استفاده کرده ایم و مقدار diff بر اساس unix است (تعداد ثانیه ها از سال 1970). در نهایت با Math.abs مطمئن شده ایم که خروجی ما عددی مثبت است. متد getUTCFullYear نیز سال را بر اساس استاندارد UTC دریافت می کند.

در آخر متد calculateAge را برای mary اجرا و در کنسول مرورگر نمایش داده ایم که می شود عدد 38 (در زمان نوشتن این مقاله). همچنین اگر خود Mary را console.log کنیم باید تمام متدهای تعریف شده را در proto مشاهده کنیم:

متد های تعریف شده در proto
متدهای تعریف شده در proto

برای متد بعدی، متد getsMarried را اضافه می کنیم:

    getsMarried(newLastName) {
        this.lastName = newLastName;
    }

قبلا به شما گفته بودم که در برخی از کشورهای غربی مثل آمریکا، زمانی که زنی ازدواج می کند نام خانوادگی همسرش را می گیرد. این متد نیز همین کار را انجام داده و نام خانوادگی اصلی را به نام خانوادگی جدید تغییر می دهد. برای تست کردن این متد می توانیم از کد زیر استفاده کنیم:

console.log(mary);

mary.getsMarried('Thompson');

console.log(mary);

خروجی شما باید به شکل زیر باشد:

تفاوت نام خانوادگی قبل و بعد از اجرای متد getsMarried
تفاوت نام خانوادگی قبل و بعد از اجرای متد getsMarried

البته هنوز متدهای استاتیک را بررسی نکرده ایم.

متدهای استاتیک

اگر به کدهای بالا دقت کنید هر زمان که بخواهیم از یک متد استفاده کنیم باید یک instance (شیء) از کلاسی را بسازیم که متد مورد نظر در آن قرار دارد. اگر getsMarried را به صورت یک تابع ساده صدا بزنیم به خاطی زیر برمی خوریم:

Uncaught ReferenceError: getsMarried is not defined

 حالا اگر نخواهیم شیء ای بسازیم و مستقیما از متد استفاده کنیم چطور؟ اینجاست که متدهای استاتیک وارد شده و به ما اجازه می دهند بدون نیاز به ساختن شیء از کلاس، از متدهای آن کلاس استفاده کنیم. برای تعریف این نوع متدها باید کلمه ی Static را به تعریف متد اضافه کنیم. مثال:

    static addNumbers (x, y) {
        return x+y;
    }

حالا اگر بخواهیم بگوییم mary.addNumbers به خطا برمی خوریم چرا که این متدها بدون instance و با نام کلاس استفاده می شوند. به طور مثال:

console.log(Person.addNumbers(1, 2));

خروجی ما در کنسول مرورگر عدد 3 خواهد بود.

ارث بری در کلاس ها در ES6

در ES6 به مبحث inheritance (ارث بری)، subclass (زیر کلاس) می گویند اما مفهوم همان مفهوم است. به کلاس زیر توجه کنید:

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  greeting() {
    return `Hello there ${this.firstName} ${this.lastName}`;
  }
}

قبلا این کلاس ساده را دیده ایم. حالا اگر بخواهیم کلاس دیگری به نام Customer داشته باشیم که اطلاعات Person را به ارث ببرد چطور؟ در ابتدا مانند دیگر زبان های برنامه نویسی باید آن را extend کنیم و سپس با استفاده از تابع super مقادیر Person را صدا بزنیم:

class Customer extends Person {
  constructor(firstName, lastName, phone, membership) {
    super(firstName, lastName);

    this.phone = phone;
    this.membership = membership;
  }

  static getMembershipCost() {
    return 500;
  }
}

زمانی که از super استفاده می کنیم، می گوییم این خصوصیات را از constructor کلاس پدر بگیر و به من بده. بقیه ی موارد را نیز باید مثل قبل وارد کنیم. در آخر این کد یک متد استاتیک را هم قرار داده ام که عدد 500 را برمی گرداند (هزینه ی عضویت). حالا برای تست کردن کد می گوییم:

const john = new Customer('John', 'Doe', '555-555-5555', 'Standard');

console.log(john.greeting());

console.log(Customer.getMembershipCost());

اگر الان از کد john.greeting استفاده کنیم هیچ مشکلی به وجود نمی آید؛ با اینکه John یک customer است اما چون زیرکلاسِ Person است می تواند از متدهای Person استفاده کند. همچنین برای استفاده از متد استاتیک خودمان هم باید از نام کلاس Customer استفاده کنیم. این مقاله آخرین مقاله ی «جاوا اسکریپت OOP» بود. امیدوارم با مفاهیم اصلی برنامه نویسی شیء گرای جاوا اسکریپت آشنا شده باشید.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری شی گرایی در جاوا اسکریپت توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (1 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

Sajjad
16 فروردین 1399
سلام و احترام، لطفا این آموزش رو به لیست آموزش های منوی سایت در بخش آموزش جاواسکریپت اضافه کنید. نه تنها این آموزش بلکه خیلی از آموزش ها به منوی سایت اضافه نشدن و باید با جستجو تو بخش مقالات پیداشون کرد! کلا Navbar این مدلی مناسب سایت شما نیست و بهتره از Mega Menu ها استفاده کنید، تشکر بابت زحماتتون.

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.