Class ها در JavaScript

مقدمه

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

کلاس ها در واقع “توابع ویژه” هستند و همانطور که می توانید عبارات تابع و اعلان های تابع را تعریف کنید، یک کلاس را می توان به دو صورت تعریف کرد: یک عبارت کلاس یا یک اعلان کلاس.

// Declaration
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// Expression; the class is anonymous but assigned to a variable
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// Expression; the class has its own name
const Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};

مانند عبارات تابع، عبارات کلاس ممکن است ناشناس باشند یا نامی متفاوت از متغیری که به آن اختصاص داده شده باشد. با این حال، برخلاف اعلان‌های تابع، اعلان‌های کلاس همان محدودیت‌های موقتی منطقه مرده مانند let یا const را دارند و طوری رفتار می‌کنند که گویی بلند نشده‌اند.

بدنه کلاس

بدنه یک کلاس بخشی است که در پرانتزهای مجعد {} قرار دارد. اینجا جایی است که اعضای کلاس مانند متدها یا سازنده را تعریف می کنید.

بدنه یک کلاس در حالت سخت گیرانه اجرا می شود، حتی بدون دستور “استفاده سخت”.

یک عنصر کلاس را می توان با سه جنبه مشخص کرد:

  • نوع: گیرنده، تنظیم کننده، روش یا میدان
  • مکان: ایستا یا نمونه
  • دید: عمومی یا خصوصی

Constructor

متد سازنده یک متد خاص برای ایجاد و مقداردهی اولیه یک شی ایجاد شده با یک کلاس است. فقط یک متد خاص با نام “constructor” در یک کلاس می تواند وجود داشته باشد – اگر کلاس حاوی بیش از یک مورد از یک متد سازنده باشد، یک SyntaxError پرتاب می شود.

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

شما می توانید ویژگی های نمونه را در داخل سازنده ایجاد کنید:

class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}

از طرف دیگر، اگر مقادیر ویژگی های نمونه شما به آرگومان های سازنده بستگی ندارد، می توانید آنها را به عنوان فیلدهای کلاس تعریف کنید.

بلوک های اولیه سازی استاتیک

بلوک‌های استاتیک مقداردهی اولیه‌سازی انعطاف‌پذیر ویژگی‌های استاتیک، از جمله ارزیابی عبارات در حین مقداردهی اولیه را می‌دهند، در حالی که دسترسی به حوزه خصوصی را می‌دهند.

چندین بلوک استاتیک را می توان اعلان کرد، و این بلوک ها را می توان با اعلان فیلدها و روش های استاتیک در هم آمیخت (همه موارد استاتیک به ترتیب اعلام ارزیابی می شوند).

Methods

متدها بر روی نمونه اولیه هر نمونه کلاس تعریف می شوند و توسط همه نمونه ها به اشتراک گذاشته می شوند. روش ها می توانند توابع ساده، توابع ناهمگام، توابع مولد یا توابع مولد ناهمگام باشند. برای اطلاعات بیشتر، به تعاریف روش مراجعه کنید.

class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
*getSides() {
yield this.height;
yield this.width;
yield this.height;
yield this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]

روش ها و زمینه های استاتیک

کلمه کلیدی static یک متد یا فیلد ثابت را برای یک کلاس تعریف می کند. خصوصیات استاتیک (فیلدها و متدها) به جای هر نمونه روی خود کلاس تعریف می شوند. روش‌های استاتیک اغلب برای ایجاد توابع کاربردی برای یک برنامه استفاده می‌شوند، در حالی که فیلدهای استاتیک برای حافظه پنهان، پیکربندی ثابت یا هر داده دیگری که نیازی به تکرار در نمونه‌ها نیست، مفید هستند.

class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755

اعلامیه های میدانی

با نحو اعلان فیلد کلاس، مثال سازنده را می توان به صورت زیر نوشت:

class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}

فیلدهای کلاس شبیه ویژگی های شی هستند نه متغیرها، بنابراین از کلمات کلیدی مانند const برای اعلام آنها استفاده نمی کنیم. در جاوا اسکریپت، خصوصیات خصوصی از نحو شناسه خاصی استفاده می کنند، بنابراین کلمات کلیدی اصلاح کننده مانند عمومی و خصوصی نیز نباید استفاده شوند.

همانطور که در بالا مشاهده شد، فیلدها را می توان با یا بدون مقدار پیش فرض اعلام کرد. فیلدهای بدون مقادیر پیش فرض به طور پیش فرض تعریف نشده است. با اعلام فیلدها در جلو، تعاریف کلاس بیشتر مستندسازی می‌شوند و فیلدها همیشه وجود دارند که به بهینه‌سازی کمک می‌کنند.

برای اطلاعات بیشتر به فیلدهای کلاس عمومی مراجعه کنید.

Private properties

با استفاده از فیلدهای خصوصی، تعریف را می توان به صورت زیر اصلاح کرد.

class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}

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

فیلدهای خصوصی را فقط می توان از قبل در یک اعلامیه فیلد اعلام کرد. آنها را نمی توان بعداً از طریق تخصیص به آنها، به روشی که ویژگی های معمولی می تواند ایجاد کرد.

برای اطلاعات بیشتر به املاک خصوصی مراجعه کنید.

ارث

کلمه کلیدی extends در اعلان های کلاس یا عبارات کلاس برای ایجاد یک کلاس به عنوان فرزند سازنده دیگر (کلاس یا تابع) استفاده می شود.

class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.

اگر سازنده ای در زیر کلاس وجود داشته باشد، قبل از استفاده از آن باید ابتدا super() را فراخوانی کند. از کلمه کلیدی super نیز می توان برای فراخوانی متدهای مربوط به کلاس فوق استفاده کرد.

class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(`${this.name} roars.`);
}
}
const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.

دستور ارزیابی

هنگامی که یک اعلان کلاس یا عبارت کلاس ارزیابی می شود، اجزای مختلف آن به ترتیب زیر ارزیابی می شوند:

  1. بند توسعه یافته، در صورت وجود، ابتدا ارزیابی می شود. باید به یک تابع سازنده معتبر یا null ارزیابی شود، در غیر این صورت یک TypeError پرتاب می شود.
  2. متد سازنده استخراج می‌شود و در صورت عدم وجود سازنده، با اجرای پیش‌فرض جایگزین می‌شود. با این حال، از آنجایی که تعریف سازنده فقط یک تعریف روش است، این مرحله قابل مشاهده نیست.
  3. کلیدهای ویژگی عناصر کلاس به ترتیب اعلان ارزیابی می شوند. اگر کلید خصوصیت محاسبه شود، عبارت محاسبه شده با این مقدار بر روی این مقدار تنظیم می شود که کلاس را احاطه کرده است (نه خود کلاس). هیچ یک از ارزش های دارایی هنوز ارزیابی نشده است.
  4. روش ها و لوازم جانبی به ترتیب اعلام نصب می شوند. متدهای نمونه و ابزارهای دسترسی بر روی ویژگی نمونه اولیه کلاس فعلی و متدهای استاتیک و ابزارهای دسترسی بر روی خود کلاس نصب می‌شوند. روش‌های نمونه خصوصی و ابزارهای دسترسی ذخیره می‌شوند تا بعداً مستقیماً روی نمونه نصب شوند. این مرحله قابل مشاهده نیست.
  5. کلاس اکنون با نمونه اولیه مشخص شده توسط extensions و پیاده سازی مشخص شده توسط سازنده مقداردهی اولیه می شود. برای تمام مراحل بالا، اگر یک عبارت ارزیابی شده سعی کند به نام کلاس دسترسی پیدا کند، یک ReferenceError پرتاب می شود زیرا کلاس هنوز مقداردهی اولیه نشده است.
  6. مقادیر عناصر کلاس به ترتیب اعلان ارزیابی می شوند:
  • برای هر فیلد نمونه (عمومی یا خصوصی)، عبارت اولیه ساز آن ذخیره می شود. مقداردهی اولیه در هنگام ایجاد نمونه، در شروع سازنده (برای کلاس های پایه) یا بلافاصله قبل از بازگشت فراخوانی super() (برای کلاس های مشتق شده) ارزیابی می شود.
  • برای هر فیلد ثابت (عمومی یا خصوصی)، مقداردهی اولیه آن با این مجموعه به خود کلاس ارزیابی می‌شود و ویژگی روی کلاس ایجاد می‌شود.
  • بلوک های اولیه سازی استاتیک با این مجموعه در خود کلاس ارزیابی می شوند.

نمونه ها

اتصال این با روش‌های نمونه و استاتیک

هنگامی که یک متد استاتیک یا نمونه بدون مقدار برای آن فراخوانی می شود، مثلاً با اختصاص متد به یک متغیر و سپس فراخوانی آن، این مقدار در داخل متد تعریف نشده خواهد بود. این رفتار یکسان است حتی اگر دستورالعمل “استفاده از سخت” وجود نداشته باشد، زیرا کد درون بدنه کلاس همیشه در حالت سخت اجرا می شود.

class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined
Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefined

اگر موارد فوق را با استفاده از نحو سنتی مبتنی بر تابع در حالت غیر دقیق بازنویسی کنیم، آنگاه فراخوانی های این روش به طور خودکار به globalThis محدود می شوند. در حالت سخت، مقدار این به صورت تعریف نشده باقی می ماند.

function Animal() {}
Animal.prototype.speak = function () {
return this;
};
Animal.eat = function () {
return this;
};
const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)
const eat = Animal.eat;
eat(); // global object (in non-strict mode)
[تعداد: 1   میانگین: 5/5]
دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

شاید دوست داشته باشید