JavaScript: الفئات والوحدات في JavaScript

مع نمو المشاريع، تحتاج أكثر من دوال ومتغيرات متناثرة — تحتاج هيكل كود منظم. الفئات توفر أنماط الكائنية، والوحدات توفر عزل الكود على مستوى الملفات. معاً، يشكلان أساس مشاريع JS الحديثة.


1. صيغة الفئة (Class)

الفئة هي قالب لإنشاء كائنات — سكر صناعي فوق دوال المُنشئ.

HTML
<script>
class ClassName {
  constructor(params) {
    this.property = params;
  }
  method() {
    // ...
  }
}
</script>

▶ مثال

JAVASCRIPT
// مثال على الكود في هذا القسم
const message = "مرحبا بالعالم!";
console.log(message);
▶ جرّب الكود

هذا أبسط مثال; الأقسام التالية ستشرح بالتفصيل.### ▶ مثال: إنشاء فئة Student

HTML
<div id="output" style="padding: 10px; border: 1px solid #ccc;"></div>

<script>
const output = document.getElementById('output');

class Student {
  constructor(name, age, grade) {
    this.name = name;
    this.age = age;
    this.grade = grade;
  }

  introduce() {
    return 'اسمي ' + this.name + '، عمري ' + this.age + ' سنة، في الصف ' + this.grade + '.';
  }

  study(subject) {
    return this.name + ' يدرس ' + subject + '.';
  }
}

const s1 = new Student('أليس', 12, 'السادس');
const s2 = new Student('بوب', 11, 'الخامس');

output.textContent = s1.introduce() + '\n' + s2.introduce() + '\n' + s1.study('الرياضيات');
</script>
💡 تلميح: class هو في الأساس سكر صناعي فوق دوال المُنشئ + البروتتويبات. new Student() يعمل بنفس طريقة new function Student() القديمة، لكن الصيغة أنظف وأكثر حدسية.



2. الخصائص والطرق

(1) خصائص وطرق المثيل

الخصائص والطرق المحددة بـ this.xxx داخل constructor تنتمي للمثيل — كل كائن يحصل على نسخته الخاصة.

(2) الطرق الثابتة (Static Methods)

الطرق المحددة بكلمة static تنتمي للفئة نفسها، وليس للمثيلات. استدعِها عبر ClassName.method().

(3) Getters و Setters

استخدم كلمتي get و set لتحديد "خصائص افتراضية" — دوال تُنفذ تلقائياً عند القراءة/الكتابة.

HTML
<div id="output" style="white-space: pre; font-family: monospace; padding: 10px; border: 1px solid #ccc; direction: ltr; text-align: left;"></div>
<script>
const output = document.getElementById('output');

class Person {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  get fullName() {
    return this.firstName + ' ' + this.lastName;
  }

  set fullName(value) {
    const parts = value.split(' ');
    this.firstName = parts[0];
    this.lastName = parts[1];
  }
}

const p = new Person('جون', 'دو');
output.textContent = 'fullName: ' + p.fullName + '\n';
p.fullName = 'جين سميث';
output.textContent += 'بعد التغيير: ' + p.fullName;
</script>

▶ مثال: الطرق الثابتة و Getters/Setters

HTML
<div id="output" style="white-space: pre; font-family: monospace; padding: 10px; border: 1px solid #ccc; direction: ltr; text-align: left;"></div>

<script>
const output = document.getElementById('output');

class Circle {
  static count = 0;

  constructor(radius) {
    this.radius = radius;
    Circle.count++;
  }

  get area() {
    return Math.PI * this.radius * this.radius;
  }

  get diameter() {
    return this.radius * 2;
  }

  set diameter(value) {
    this.radius = value / 2;
  }

  static createUnit() {
    return new Circle(1);
  }
}

const c1 = new Circle(5);
const c2 = new Circle(10);
const c3 = Circle.createUnit();

output.textContent = 'دائرة بنصف قطر 5:\n';
output.textContent += '  المساحة: ' + c1.area.toFixed(2) + '\n';
output.textContent += '  القطر: ' + c1.diameter + '\n';
output.textContent += '  تعيين القطر إلى 20:\n';
c1.diameter = 20;
output.textContent += '  نصف القطر الجديد: ' + c1.radius + '\n\n';
output.textContent += 'إجمالي الدوائر المنشأة: ' + Circle.count + '\n';
output.textContent += 'نصف قطر الدائرة الوحدة: ' + c3.radius;
</script>
▶ جرّب الكود

3. الوراثة

extends تتيح لفئة الابن أن ترث خصائص وطرق من فئة الأب. super يستدعي مُنشئ الأب أو طرقه.

▶ مثال: عرض الوراثة

HTML
<div id="output" style="white-space: pre; font-family: monospace; padding: 10px; border: 1px solid #ccc; direction: ltr; text-align: left;"></div>

<script>
const output = document.getElementById('output');

class Animal {
  constructor(name, sound) {
    this.name = name;
    this.sound = sound;
  }

  speak() {
    return this.name + ' يقول: ' + this.sound;
  }
}

class Dog extends Animal {
  constructor(name, breed) {
    super(name, 'نباح');
    this.breed = breed;
  }

  fetch(item) {
    return this.name + ' أحضر ' + item;
  }
}

class Cat extends Animal {
  constructor(name, indoor) {
    super(name, 'مواء');
    this.indoor = indoor;
  }

  purr() {
    return this.name + ' يخرخر...';
  }
}

const dog = new Dog('ريكس', 'شيبا إينو');
const cat = new Cat('شارب', true);

output.textContent = dog.speak() + '\n';
output.textContent += dog.fetch('فريسبي') + '\n';
output.textContent += cat.speak() + '\n';
output.textContent += cat.purr();
</script>
▶ جرّب الكود
💡 تلميح: في constructor فئة الابن، يجب استدعاء super() قبل استخدام this — لأنه حتى يتم تهيئة الأب، this للابن غير موجود. هذه القاعدة الأكثر سهولة للنسيان عند تعلم الوراثة.



4. وحدات ES (ES Modules)

الوحدات هي وحدة تنظيم الكود في JS. وحدة واحدة = ملف واحد. المتغيرات داخل الوحدة خاصة بشكل افتراضي — فقط العناصر المُصدّرة صراحة يمكن الوصول إليها خارجياً.

(1) التصدير/الاستيراد المُسمى

HTML
<script>
// math.js
export const PI = 3.14;
export function add(a, b) { return a + b; }

// app.js
import { PI, add } from './math.js';
</script>

(2) التصدير/الاستيراد الافتراضي

HTML
<script>
// logger.js
export default function log(msg) { console.log(msg); }

// app.js
import log from './logger.js';
</script>

(3) استخدام الوحدات في HTML

ببساطة أضف type="module" إلى وسم script.

HTML
<script type="module">
  import { add } from './math.js';
  console.log(add(1, 2));
</script>
⚠️ ملاحظة: ملفات الوحدات تُخزن مؤقتاً بواسطة المتصفحات وتخضع لسياسة نفس الأصل. فتح ملف HTML محلياً قد يفشل في تحميل الوحدات — تحتاج لخدمته عبر خادم HTTP.

▶ مثال: محاكاة وحدة ملف واحد (وحدة مضمنة)

في المشاريع الحقيقية، يجب تقسيم الوحدات إلى ملفات منفصلة. هنا نحاكيها داخل ملف HTML واحد باستخدام type="module".

HTML
<div id="output" style="padding: 10px; border: 1px solid #ccc;"></div>

<script type="module">
const output = document.getElementById('output');

const calculator = {
  add(a, b) { return a + b; },
  subtract(a, b) { return a - b; },
  multiply(a, b) { return a * b; },
  divide(a, b) { return b !== 0 ? a / b : 'لا يمكن القسمة على صفر'; }
};

const formatter = {
  currency(value) { return '$' + value.toFixed(2); },
  percent(value) { return (value * 100).toFixed(1) + '%'; }
};

const r1 = calculator.add(10, 20);
const r2 = calculator.multiply(5, 4);
const r3 = calculator.divide(10, 3);

output.textContent = '10 + 20 = ' + r1 + '\n';
output.textContent += '5 × 4 = ' + r2 + '\n';
output.textContent += '10 ÷ 3 = ' + formatter.currency(r3) + '\n';
output.textContent += '0.85 → ' + formatter.percent(0.85);

// في مشروع حقيقي ستقسم هكذا:
// calculator.js → export { calculator }
// formatter.js → export { formatter }
// main.js → import { calculator } from './calculator.js'
//           import { formatter } from './formatter.js'
</script>
▶ جرّب الكود

❓ أسئلة شائعة

س ما الفرق الحقيقي بين الفئة ودالة المُنشئ؟
ج لا فرق جوهري — الفئة سكر صناعي. لكن الفئات لديها بعض السمات: يجب استدعاؤها بـ new (لا يمكن تشغيلها كدوال عادية)، الطرق غير قابلة للعد، والوضع الصارم مُفعّل افتراضياً. الصيغة أنظف، لذا تُوصى بالفئات.
س ما الفرق بين import و require؟
ج import هو صيغة وحدات ES — يُحلل ثابتاً وقت التجميع. require هو CommonJS (Node.js) — يُحمّل ديناميكياً وقت التشغيل. المتصفحات تدعم أصلاً فقط وحدات ES. import يجب أن يكون في المستوى الأعلى؛ require يمكن أن يكون في أي مكان.
س هل يمكنك خلط التصدير الافتراضي والمُسمى؟
ج نعم. وحدة يمكن أن يكون لها تصدير افتراضي واحد و عدة تصديرات مُسمى: export default App; export const utils = {};. استورد بـ: import App, { utils } from './module.js'.

📖 ملخص

📝 تمارين

  1. أساسي: أنشئ فئة Rectangle بخاصيتي width و height، بالإضافة إلى طريقتي getArea() و getPerimeter().
  2. متوسط: أضف get area() getter و set area(value) setter إلى Rectangle (تعيين المساحة يعدّل العرض/الارتفاع بشكل متناسب). ثم أنشئ فئة فرعية Square تمتد Rectangle.
  3. تحدي: صمم فئة EventManager بطرقتَي on(event, callback) و off(event, callback) و emit(event, data) لمحاكاة نظام أحداث بسيط (تلميح: استخدم كائن يُعيّن الأحداث إلى مصفوفات استدعاءات).
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%