برنامه‌نویسی شیءگرا

مفاهیم OOP، Constructor Function و Class، متدها و فیلدها، متدها و فیلدهای ایستا و خصوصی، Inheritance با extends و super، بازنویسی متدها، Polymorphism، Encapsulation، Composition و Mixins.

برنامه‌نویسی شیءگرا چیست؟

شیءگرایی (OOP) یک پارادایم برنامه‌نویسی است که برنامه را حول اشیا (Objects) سازماندهی می‌کند — هر شیء داده‌ها (ویژگی‌ها) و رفتارها (متدها) را در یک واحد ترکیب می‌کند.

چهار اصل اصلی شیءگرایی:

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()); // سلام، من سارا هستم
کلاس در جاوا اسکریپت فقط سینتکس شیرین (Syntactic Sugar) روی Prototype است — در پشت صحنه همان Constructor Function کار می‌کند.

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 ارث می‌برند

جمع‌بندی

مفهومابزارمثال
تعریف کلاسclassclass Person {}
ارث‌بریextendsclass Dog extends Animal {}
دسترسی به والدsupersuper(name)
خصوصی‌سازی#field#balance = 0
متد ایستاstaticstatic create() {}
ترکیبMixinsextends Mixin(Base)

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط