404 Not Found

404 Not Found


nginx

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

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


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

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

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

مثال: إنشاء فئة 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() القديمة، لكن الصيغة أنظف وأكثر حدسية.


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

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

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

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

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

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>
▶ جرّب الكود

الوراثة

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 للابن غير موجود. هذه القاعدة الأكثر سهولة للنسيان عند تعلم الوراثة.


وحدات ES (ES Modules)

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

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

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>

التصدير/الاستيراد الافتراضي

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

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

استخدام الوحدات في 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>
▶ جرّب الكود

📖 ملخص

  1. class هو سكر صناعي فوق المُنشئات؛ constructor هو المُهيئ، والطرق تُحدد على البروتتويب
  2. الطرق static تنتمي للفئة، وليس للمثيلات — مثالية لطرق الأدوات والمصانع
  3. get/set يحددان خصائص افتراضية تعتراض القراءات/ال الكتابة للتحقق أو الحساب
  4. extends تتيح الوراثة؛ constructor الابن يجب أن يستدعي super() أولاً
  5. وحدات ES تستخدم export/import لعزل الكود وإعادة استخدامه؛ type="module" تفعّلها في HTML
  6. الوحدات تنظم الكود حسب الميزة، تتيح التحميل الكسول، وتمنع تلوث مساحة الأسماء العامة

❓ أسئلة شائعة

س ما الفرق الحقيقي بين الفئة ودالة المُنشئ؟
ج لا فرق جوهري — الفئة سكر صناعي. لكن الفئات لديها بعض السمات: يجب استدعاؤها بـ 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) لمحاكاة نظام أحداث بسيط (تلميح: استخدم كائن يُعيّن الأحداث إلى مصفوفات استدعاءات).
100%