JavaScript المتقدم (ES6+)

فئات ES6

13 دقيقة الدرس 26 من 40

فئات ES6

قدمت ES6 صيغة جديدة لإنشاء الكائنات وتطبيق الوراثة في JavaScript. توفر الفئات صيغة أنظف وأكثر سهولة لدوال المُنشئ والوراثة النموذجية، مما يجعل البرمجة الكائنية في JavaScript أكثر سهولة.

ما هي فئات ES6؟

الفئات هي قوالب لإنشاء الكائنات. تقوم بتغليف البيانات مع الكود للعمل على تلك البيانات. فئات ES6 هي سكر نحوي فوق الوراثة القائمة على النموذج الأولي الموجودة في JavaScript.

مفهوم أساسي: فئات ES6 لا تقدم نموذج برمجة كائنية جديد لـ JavaScript. إنها مجرد صيغة أنظف للوراثة القائمة على النموذج الأولي التي كانت لدينا دائماً.

إعلان الفئة

يمكنك إعلان فئة باستخدام الكلمة المفتاحية class:

// إعلان الفئة class Person { constructor(name, age) { this.name = name; this.age = age; } greet() { return `Hello, I'm ${this.name} and I'm ${this.age} years old.`; } } // إنشاء نسخ const john = new Person("John", 30); const sarah = new Person("Sarah", 25); console.log(john.greet()); // الناتج: "Hello, I'm John and I'm 30 years old." console.log(sarah.greet()); // الناتج: "Hello, I'm Sarah and I'm 25 years old."

دالة المُنشئ (Constructor)

دالة constructor هي دالة خاصة لإنشاء وتهيئة الكائنات المُنشأة بواسطة الفئة. يمكن أن يكون هناك دالة مُنشئ واحدة فقط لكل فئة.

class Car { constructor(brand, model, year) { this.brand = brand; this.model = model; this.year = year; this.mileage = 0; // قيمة افتراضية } displayInfo() { return `${this.year} ${this.brand} ${this.model}`; } drive(miles) { this.mileage += miles; return `Drove ${miles} miles. Total mileage: ${this.mileage}`; } } const myCar = new Car("Toyota", "Camry", 2023); console.log(myCar.displayInfo()); // "2023 Toyota Camry" console.log(myCar.drive(100)); // "Drove 100 miles. Total mileage: 100" console.log(myCar.drive(50)); // "Drove 50 miles. Total mileage: 150"
نصيحة: يتم استدعاء المُنشئ تلقائياً عند إنشاء نسخة جديدة باستخدام الكلمة المفتاحية new. لا تحتاج لاستدعائه يدوياً.

دوال النسخة (Instance Methods)

الدوال المُعرَّفة داخل الفئة تُضاف إلى النموذج الأولي وتُشارك بواسطة جميع النسخ:

class Calculator { constructor() { this.result = 0; } add(num) { this.result += num; return this; } subtract(num) { this.result -= num; return this; } multiply(num) { this.result *= num; return this; } divide(num) { if (num !== 0) { this.result /= num; } return this; } clear() { this.result = 0; return this; } getValue() { return this.result; } } const calc = new Calculator(); const result = calc.add(10).multiply(2).subtract(5).getValue(); console.log(result); // 15
تسلسل الدوال: من خلال إرجاع this من الدوال، يمكنك ربط استدعاءات الدوال معاً للحصول على كود أكثر قابلية للقراءة.

الدوال والخصائص الثابتة (Static)

الدوال والخصائص الثابتة تنتمي إلى الفئة نفسها، وليس إلى النسخ. إنها مفيدة لدوال الأدوات المرتبطة بالفئة:

class MathHelper { static PI = 3.14159; static square(num) { return num * num; } static cube(num) { return num * num * num; } static circleArea(radius) { return this.PI * this.square(radius); } } // استدعِ الدوال الثابتة على الفئة، وليس النسخ console.log(MathHelper.square(5)); // 25 console.log(MathHelper.cube(3)); // 27 console.log(MathHelper.circleArea(10)); // 314.159 console.log(MathHelper.PI); // 3.14159 // الدوال الثابتة غير متاحة على النسخ const helper = new MathHelper(); // helper.square(5); // TypeError: helper.square is not a function

حقول الفئة (العامة والخاصة)

يدعم JavaScript الحديث حقول الفئة العامة والخاصة:

class BankAccount { // حقل عام accountType = "Savings"; // حقل خاص (يبدأ بـ #) #balance = 0; #pin; constructor(initialBalance, pin) { this.#balance = initialBalance; this.#pin = pin; } // دالة عامة deposit(amount) { if (amount > 0) { this.#balance += amount; return `Deposited $${amount}. New balance: $${this.#balance}`; } return "Invalid amount"; } // دالة خاصة #validatePin(pin) { return pin === this.#pin; } withdraw(amount, pin) { if (!this.#validatePin(pin)) { return "Invalid PIN"; } if (amount > this.#balance) { return "Insufficient funds"; } this.#balance -= amount; return `Withdrew $${amount}. New balance: $${this.#balance}`; } getBalance(pin) { if (this.#validatePin(pin)) { return `Current balance: $${this.#balance}`; } return "Invalid PIN"; } } const account = new BankAccount(1000, "1234"); console.log(account.deposit(500)); // "Deposited $500. New balance: $1500" console.log(account.getBalance("1234")); // "Current balance: $1500" console.log(account.withdraw(300, "1234")); // "Withdrew $300. New balance: $1200" // لا يمكن الوصول إلى الحقول الخاصة // console.log(account.#balance); // SyntaxError // account.#validatePin("1234"); // SyntaxError
مهم: يجب التصريح عن الحقول الخاصة في جسم الفئة قبل استخدامها. لا يمكن الوصول إليها أو تعديلها من خارج الفئة.

Getters و Setters

تسمح لك Getters و Setters بالتحكم في الوصول إلى خصائص الكائن:

class Temperature { constructor(celsius) { this._celsius = celsius; } // Getter get celsius() { return this._celsius; } // Setter set celsius(value) { if (value < -273.15) { console.log("Temperature cannot be below absolute zero"); return; } this._celsius = value; } get fahrenheit() { return (this._celsius * 9/5) + 32; } set fahrenheit(value) { this.celsius = (value - 32) * 5/9; } get kelvin() { return this._celsius + 273.15; } set kelvin(value) { this.celsius = value - 273.15; } } const temp = new Temperature(25); console.log(temp.celsius); // 25 console.log(temp.fahrenheit); // 77 console.log(temp.kelvin); // 298.15 temp.fahrenheit = 86; console.log(temp.celsius); // 30 temp.kelvin = 300; console.log(temp.celsius); // 26.85

تعبيرات الفئة

مثل الدوال، يمكن أيضاً تعريف الفئات باستخدام التعبيرات:

// تعبير فئة مُسمى const Rectangle = class Rect { constructor(width, height) { this.width = width; this.height = height; } area() { return this.width * this.height; } }; // تعبير فئة مجهول const Circle = class { constructor(radius) { this.radius = radius; } area() { return Math.PI * this.radius * this.radius; } }; const rect = new Rectangle(10, 5); console.log(rect.area()); // 50 const circle = new Circle(7); console.log(circle.area()); // 153.938...

الفئات مقابل دوال المُنشئ

إليك كيفية مقارنة فئات ES6 بطريقة دالة المُنشئ التقليدية:

// الطريقة القديمة: دالة المُنشئ function PersonOld(name, age) { this.name = name; this.age = age; } PersonOld.prototype.greet = function() { return `Hello, I'm ${this.name}`; }; // الطريقة الجديدة: فئة ES6 class PersonNew { constructor(name, age) { this.name = name; this.age = age; } greet() { return `Hello, I'm ${this.name}`; } } // كلاهما ينشئ كائنات متكافئة وظيفياً const person1 = new PersonOld("John", 30); const person2 = new PersonNew("Jane", 25); console.log(person1.greet()); // "Hello, I'm John" console.log(person2.greet()); // "Hello, I'm Jane"
أفضل ممارسة: استخدم فئات ES6 للكود الجديد. إنها أنظف وأسهل في القراءة وتتماشى مع معايير JavaScript الحديثة. ومع ذلك، فهم دوال المُنشئ لا يزال مهماً لقراءة الكود القديم.

تمرين تطبيقي:

التحدي: أنشئ فئة Book بالمتطلبات التالية:

  • خصائص: title, author, pages, currentPage (تبدأ من 0)
  • دالة read(pages): تزيد currentPage
  • دالة getProgress(): تُرجع نسبة القراءة المئوية
  • Getter isFinished: يُرجع true إذا اكتمل الكتاب
  • دالة ثابتة compare(book1, book2): تُرجع أي كتاب يحتوي على صفحات أكثر

الحل:

class Book { constructor(title, author, pages) { this.title = title; this.author = author; this.pages = pages; this.currentPage = 0; } read(pages) { this.currentPage = Math.min(this.currentPage + pages, this.pages); return `Reading... Currently on page ${this.currentPage}`; } getProgress() { const percentage = (this.currentPage / this.pages * 100).toFixed(1); return `${percentage}% complete`; } get isFinished() { return this.currentPage >= this.pages; } static compare(book1, book2) { if (book1.pages > book2.pages) { return `"${book1.title}" is longer`; } else if (book2.pages > book1.pages) { return `"${book2.title}" is longer`; } return "Both books have the same number of pages"; } } const book = new Book("JavaScript: The Good Parts", "Douglas Crockford", 176); console.log(book.read(50)); // "Reading... Currently on page 50" console.log(book.getProgress()); // "28.4% complete" console.log(book.isFinished); // false const book2 = new Book("Eloquent JavaScript", "Marijn Haverbeke", 472); console.log(Book.compare(book, book2)); // "Eloquent JavaScript" is longer

الملخص

في هذا الدرس، تعلمت:

  • فئات ES6 توفر صيغة أنظف للبرمجة الكائنية
  • دالة المُنشئ تُهيّئ النسخ الجديدة
  • دوال النسخة تُشارك بواسطة جميع النسخ عبر النموذج الأولي
  • الدوال الثابتة تنتمي إلى الفئة نفسها، وليس النسخ
  • الحقول الخاصة (#) توفر تغليف حقيقي
  • Getters و Setters تتحكم في الوصول إلى الخصائص
  • الفئات هي سكر نحوي فوق الوراثة القائمة على النموذج الأولي
التالي: في الدرس التالي، سنستكشف وراثة الفئات وكيفية إنشاء تسلسلات هرمية للفئات باستخدام الكلمة المفتاحية extends!