البرمجة الكائنية في لغة JavaScript
أهلاً يا شباب! اليوم بنتكلم عن موضوع مهم جداً في عالم JavaScript وهو البرمجة الكائنية (Object-Oriented Programming أو OOP). لا تخافوا من الاسم، الموضوع أبسط مما تتخيلون ومفيد جداً لتنظيم الكود.
ليش نستخدم OOP في JavaScript؟
الـ OOP بتساعدنا نكتب كود منظم، سهل الصيانة، وقابل لإعادة الاستخدام. بدل ما يكون الكود كله فوق بعضه، بنقسمه لكائنات (Objects) كل كائن له خصائصه ووظائفه الخاصة فيه. تخيل إنك بتبني سيارة، بدل ما تكون كل قطعة لوحدها، بتحط المحرك في كائن، العجلات في كائن، وهكذا. كل كائن مسؤول عن جزء معين.
الكائنات (Objects) هي الأساس
في JavaScript، كل شيء تقريباً كائن! من النصوص (Strings) للأرقام (Numbers) وحتى الدوال (Functions). الكائن هو مجرد مجموعة من الخصائص (Properties) والوظائف (Methods).
إنشاء الكائنات (Creating Objects)
1. Object Literals (أسهل طريقة)
هذي أسرع طريقة عشان تسوي كائن واحد:
ملاحظة: Object Literals ممتازة للكائنات اللي ما تحتاج تنسخها كثير.
const car = {
brand: 'Toyota',
model: 'Camry',
year: 2023,
start: function() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is starting...</code>;
},
stop() { // طريقة مختصرة لكتابة الدوال
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is stopping.</code>;
}
};
console.log(car.brand); // Toyota
console.log(car.start()); // Toyota Camry is starting...
2. Constructor Functions (قبل ES6 Classes)
لو عندك كائنات كثيرة من نفس النوع (مثلاً، عندك 100 سيارة وكلهم لهم نفس الخصائص والوظائف الأساسية)، بتستخدم الـ Constructor Functions. هذي الدوال هي اللي "بتبني" الكائنات لك.
تذكر تستخدم
thisعشان تشير للخصائص والوظائف الخاصة بالكائن اللي قاعد تبنيه.
function Car(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
this.start = function() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is starting...</code>;
};
}
const myCar = new Car('Honda', 'Civic', 2022);
const anotherCar = new Car('BMW', 'X5', 2024);
console.log(myCar.brand); // Honda
console.log(anotherCar.start()); // BMW X5 is starting...
ملاحظة: الدوال مثل start بيتم إنشاؤها لكل كائن جديد، وهذا ممكن يكون غير فعال لو عندك عدد كبير من الكائنات. عشان كذا، نستخدم الـ prototype.
function CarPrototype(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
}
// إضافة الدوال للـ prototype عشان كل الكائنات تشارك نفس الدالة
CarPrototype.prototype.start = function() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is starting...</code>;
};
CarPrototype.prototype.stop = function() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is stopping.</code>;
};
const car1 = new CarPrototype('Nissan', 'Altima', 2021);
console.log(car1.start()); // Nissan Altima is starting...
3. ES6 Classes (الطريقة الحديثة والأكثر وضوحاً)
مع ES6، جات الـ class keyword اللي بتخلي كتابة Constructor Functions والـ prototype أسهل وأوضح، وهي مجرد "سكر نحوي" (syntactic sugar) فوق الـ prototypes.
class Vehicle {
constructor(brand, model, year) {
this.brand = brand;
this.model = model;
this.year = year;
}
start() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is starting...</code>;
}
stop() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is stopping.</code>;
}
}
const myVehicle = new Vehicle('Mercedes', 'C-Class', 2023);
console.log(myVehicle.start()); // Mercedes C-Class is starting...
التغليف (Encapsulation)
التغليف يعني إننا نجمع الخصائص (مثل brand, model) والدوال (مثل start, stop) اللي تخص كائن معين في مكان واحد (الكائن نفسه). هذا بيخلي الكود منظم وسهل الفهم والتعديل. الكلاسات والـ Constructor Functions هي أمثلة قوية للتغليف.
الوراثة (Inheritance)
الوراثة بتخلي كائن جديد (Child Class) يرث الخصائص والوظائف من كائن موجود (Parent Class)، وهذا بيوفر عليك إعادة كتابة الكود. تخيل عندك Vehicle (مركبة)، ممكن تسوي Car (سيارة) و Motorcycle (دراجة نارية) يرثون من Vehicle.
الوراثة باستخدام Classes (ES6 extends)
هذي هي الطريقة الأكثر استخداماً للوراثة حالياً.
class Car extends Vehicle { // Car ترث من Vehicle
constructor(brand, model, year, doors) {
super(brand, model, year); // استدعاء constructor الكائن الأب (Vehicle)
this.doors = doors;
}
honk() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is honking!</code>;
}
}
class ElectricCar extends Car {
constructor(brand, model, year, doors, batteryLife) {
super(brand, model, year, doors);
this.batteryLife = batteryLife;
}
charge() {
return <code dir="ltr" style="background:#f3f4f6; color:#0056b3; padding:2px 6px; border-radius:4px; font-family:monospace; direction:ltr !important; display:inline-block;">${this.brand} ${this.model} is charging. Battery life: ${this.batteryLife} hours.</code>;
}
}
const myCarObj = new Car('Ford', 'Mustang', 2020, 2);
console.log(myCarObj.start()); // Ford Mustang is starting... (دالة من Vehicle)
console.log(myCarObj.honk()); // Ford Mustang is honking! (دالة خاصة بـ Car)
const tesla = new ElectricCar('Tesla', 'Model 3', 2023, 4, 10);
console.log(tesla.start()); // Tesla Model 3 is starting...
console.log(tesla.charge()); // Tesla Model 3 is charging. Battery life: 10 hours.
نصيحة:
super()لازم تستدعيها قبل ما تستخدمthisفي الـconstructorتبع الكلاس الابن.
تعدد الأشكال (Polymorphism)
تعدد الأشكال يعني إن الكائنات المختلفة ممكن تستجيب لنفس الرسالة (نفس اسم الدالة) بطرق مختلفة. مثلاً، لو عندك دالة makeSound()، ممكن Dog يعمل Woof! و Cat تعمل Meow!. في JavaScript، هذا بيتحقق بشكل طبيعي عن طريق Overriding (إعادة تعريف) الدوال في الكلاسات الابنة.
class Animal {
makeSound() {
return "The animal makes a sound.";
}
}
class Dog extends Animal {
makeSound() {
return "Woof! Woof!"; // Override makeSound()
}
}
class Cat extends Animal {
makeSound() {
return "Meow!"; // Override makeSound()
}
}
const genericAnimal = new Animal();
const doggy = new Dog();
const kitty = new Cat();
console.log(genericAnimal.makeSound()); // The animal makes a sound.
console.log(doggy.makeSound()); // Woof! Woof!
console.log(kitty.makeSound()); // Meow!
خاتمة
البرمجة الكائنية في JavaScript أداة قوية جداً بتخليك تكتب كود أفضل وأكثر كفاءة. ركز على فهم الكائنات، التغليف، والوراثة، ومع الممارسة بتصير محترف فيها. بالتوفيق!