برنامهنویسی شیءگرا
مفاهیم OOP، Constructor Function و Class، متدها و فیلدها، متدها و فیلدهای ایستا و خصوصی، Inheritance با extends و super، بازنویسی متدها، Polymorphism، Encapsulation، Composition و Mixins.
برنامهنویسی شیءگرا چیست؟
شیءگرایی (OOP) یک پارادایم برنامهنویسی است که برنامه را حول اشیا (Objects) سازماندهی میکند — هر شیء دادهها (ویژگیها) و رفتارها (متدها) را در یک واحد ترکیب میکند.
چهار اصل اصلی شیءگرایی:
- Encapsulation: پنهانسازی جزئیات داخلی و ارائه رابط عمومی
- Inheritance: ساخت کلاسهای جدید بر پایه کلاسهای موجود
- Polymorphism: یک رابط، پیادهسازیهای مختلف
- Abstraction: نمایش ویژگیهای ضروری و پنهان کردن پیچیدگی
Constructor Function — روش قدیمی
قبل از ES6، اشیا با تابع سازنده ساخته میشدند:
function Person(name, age) {
this.name = name;
this.age = age;
}
// متد به prototype اضافه میشود (برای همه نمونهها مشترک):
Person.prototype.greet = function () {
return "سلام، من " + this.name + " هستم";
};
const ali = new Person("علی", 25);
console.log(ali.greet()); // سلام، من علی هستم
console.log(ali instanceof Person); // true
Class — کلاس در ES6
کلاسها در ES6 معرفی شدند — سینتکس تمیزتر برای الگوی Constructor:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
greet() {
return "سلام، من " + this.name + " هستم";
}
}
const sara = new Person("سارا", 30);
console.log(sara.greet()); // سلام، من سارا هستم
Constructor در کلاس
متد constructor هنگام new بهصورت خودکار اجرا میشود:
class Rectangle {
constructor(width, height) {
this.width = width;
this.height = height;
}
getArea() {
return this.width * this.height;
}
}
const rect = new Rectangle(10, 5);
console.log(rect.getArea()); // 50
Getter و Setter در کلاس
با get و set به ویژگیها دسترسی کنترلشده میدهیم:
class Temperature {
constructor(celsius = 0) {
this.celsius = celsius;
}
get fahrenheit() {
return this.celsius * 9 / 5 + 32;
}
set fahrenheit(value) {
this.celsius = (value - 32) * 5 / 9;
}
}
const temp = new Temperature(25);
console.log(temp.fahrenheit); // 77
temp.fahrenheit = 32;
console.log(temp.celsius); // 0
فیلدهای کلاس (Class Fields)
از ES2022 میتوان فیلدها را مستقیم در کلاس تعریف کرد:
class Counter {
count = 0; // فیلد عمومی (مقدار اولیه)
#secret = "مخفی"; // فیلد خصوصی (Private)
increment() {
this.count++;
return this;
}
reveal() {
return this.#secret;
}
}
const c = new Counter();
c.increment().increment();
console.log(c.count); // 2
console.log(c.reveal()); // مخفی
// console.log(c.#secret); // SyntaxError — خارج از کلاس قابل دسترسی نیست
متدهای ایستا (Static Methods)
متدهای static روی خود کلاس صدا زده میشوند نه نمونه:
class MathUtils {
static square(x) {
return x * x;
}
static cube(x) {
return x * x * x;
}
}
console.log(MathUtils.square(4)); // 16
console.log(MathUtils.cube(3)); // 27
// const m = new MathUtils();
// m.square(4); // TypeError — متد ایستا روی نمونه نیست
فیلدهای ایستا (Static Fields)
class User {
static totalUsers = 0; // فیلد ایستا — بین همه نمونهها مشترک
static MAX_AGE = 120;
constructor(name) {
this.name = name;
User.totalUsers++; // شمارنده مشترک
}
}
new User("علی");
new User("سارا");
console.log(User.totalUsers); // 2
console.log(User.MAX_AGE); // 120
فیلدهای خصوصی (Private Fields)
با پیشوند # — دسترسی خارج از کلاس ممکن نیست. این Encapsulation واقعی است:
class BankAccount {
#balance = 0; // واقعاً خصوصی!
deposit(amount) {
if (amount <= 0) throw new Error("مبلغ باید مثبت باشد");
this.#balance += amount;
return this.#balance;
}
withdraw(amount) {
if (amount > this.#balance) throw new Error("موجودی کافی نیست");
this.#balance -= amount;
return this.#balance;
}
get balance() {
return this.#balance;
}
}
const acc = new BankAccount();
acc.deposit(1000);
acc.withdraw(300);
console.log(acc.balance); // 700
// console.log(acc.#balance); // SyntaxError
// acc.balance = 999999; // خطا — setter نداریم
Inheritance — وراثت با extends
کلاس فرزند با extends از کلاس والد ارثبری میکند:
class Animal {
constructor(name) {
this.name = name;
}
speak() {
return this.name + " صدا میکند";
}
}
class Dog extends Animal {
fetch() {
return this.name + " توپ را میآورد";
}
}
const rex = new Dog("رکس");
console.log(rex.speak()); // رکس صدا میکند
console.log(rex.fetch()); // رکس توپ را میآورد
console.log(rex instanceof Dog); // true
console.log(rex instanceof Animal); // true — سلسلهمراتب
super — دسترسی به والد
در constructor فرزند، super() باید قبل از this صدا زده شود:
class Animal {
constructor(name) {
this.name = name;
}
}
class Bird extends Animal {
constructor(name, canFly) {
super(name); // اجرای constructor والد
this.canFly = canFly;
}
describe() {
return this.name + (this.canFly ? " پرواز میکند" : " پرواز نمیکند");
}
}
const pigeon = new Bird("کبوتر", true);
console.log(pigeon.describe()); // کبوتر پرواز میکند
بازنویسی متدها (Method Overriding)
فرزند میتواند متد والد را با همان نام بازنویسی کند:
class Animal {
speak() {
return "صدای حیوان";
}
}
class Cat extends Animal {
speak() {
return "میو میو!";
}
}
class PersianCat extends Cat {
speak() {
return super.speak() + " (صدای بلند)";
}
}
console.log(new Animal().speak()); // صدای حیوان
console.log(new Cat().speak()); // میو میو!
console.log(new PersianCat().speak()); // میو میو! (صدای بلند)
Polymorphism — چندریختی
یک رابط مشترک، رفتارهای مختلف — کد بدون دانستن نوع دقیق کار میکند:
class Shape {
area() {
return 0;
}
}
class Circle extends Shape {
constructor(r) { super(); this.r = r; }
area() { return Math.PI * this.r ** 2; }
}
class Square extends Shape {
constructor(a) { super(); this.a = a; }
area() { return this.a ** 2; }
}
// یک تابع برای همه اشکال:
function printArea(shape) {
console.log("مساحت: " + shape.area().toFixed(2));
}
printArea(new Circle(5)); // مساحت: 78.54
printArea(new Square(4)); // مساحت: 16.00
Composition — ترکیب
«IS-A را با ارثبری، HAS-A را با ترکیب پیاده کن» — ترکیب انعطاف بیشتری دارد:
// قابلیتها بهصورت توابع مستقل:
const canFly = obj => ({ ...obj, fly: () => obj.name + " پرواز میکند" });
const canSwim = obj => ({ ...obj, swim: () => obj.name + " شنا میکند" });
// ترکیب قابلیتها:
const duck = canSwim(canFly({ name: "اردک" }));
console.log(duck.fly()); // اردک پرواز میکند
console.log(duck.swim()); // اردک شنا میکند
Mixins — میکسینها
روش کلاسیک ترکیب قابلیتها بدون ارثبری — متدها به prototype اضافه میشوند:
// سازنده میکسین:
const Serializable = Base => class extends Base {
serialize() {
return JSON.stringify(this);
}
};
const Timestamped = Base => class extends Base {
created_at = new Date().toISOString();
};
// ترکیب چند قابلیت:
class User {}
class SmartUser extends Serializable(Timestamped(User)) {}
const u = new SmartUser();
u.name = "علی";
console.log(u.created_at); // 2025-01-15T10:30:00.000Z
console.log(u.serialize()); // {"name":"علی","created_at":"..."}
instanceof و سلسلهمراتب
class A {}
class B extends A {}
class C extends B {}
const c = new C();
console.log(c instanceof C); // true
console.log(c instanceof B); // true
console.log(c instanceof A); // true
console.log(c instanceof Object); // true — همه از Object ارث میبرند
جمعبندی
| مفهوم | ابزار | مثال |
|---|---|---|
| تعریف کلاس | class | class Person {} |
| ارثبری | extends | class Dog extends Animal {} |
| دسترسی به والد | super | super(name) |
| خصوصیسازی | #field | #balance = 0 |
| متد ایستا | static | static create() {} |
| ترکیب | Mixins | extends Mixin(Base) |
این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.