JavaScript ES2026 · كورس عربي شامل

تعلمJavaScript

ECMAScript 2026 · JUNE 2026

HTML بنى الهيكل، CSS لبسه الجمال — وJavaScript هتديله عقل وروح.
هنا هتتعلم إزاي تفكر زي مبرمج حقيقي، مش بس تكتب كود يشتغل.

🧠
// طريقة المذاكرة الصح في JavaScript

JavaScript مختلفة عن HTML وCSS — هنا فيه منطق. القفزة الكبرى مش في حفظ الـ syntax، هي في بناء "Mental Model" — تتخيل في دماغك إيه اللي بيحصل لما الكود يشتغل سطر سطر. كل درس هنا هيشرحلك مش بس "إزاي تكتب" لكن "ليه بيشتغل كده". اكتب كل كود بإيدك، افتح Console بتاع المتصفح (F12) وجرّب، وكسّر الكود قصداً عشان تتعلم من الأخطاء.

// أدوات هتحتاجها طول الكورس
F12
فتح DevTools — بيتك التاني في JavaScript
CtrlShiftJ
فتح Console مباشرةً في Chrome
CtrlSpace
Autocomplete في VS Code — يوفر وقت كبير
F5
Debugger — تشغيل خطوة بخطوة في VS Code
Ctrl`
فتح Terminal جوّا VS Code
AltClick
Multi-cursor — تعديل أكتر من سطر مرة واحدة
CtrlShiftP
Command Palette — كل أوامر VS Code
CtrlF5
Hard Refresh — لو غيّرت كود وما اتحدثش
// مشروع الدبلومة — المرحلة الأخيرة
Portfolio Page — إحياء صفحتك
هتاخد نفس الـ HTML وCSS اللي بنيتهم، وتضيف ليهم منطق وتفاعل حقيقي
HTML ✓
CSS ✓
JS ◄
8
وحدات
35+
درس
12
تاسكات
10+
مطبات برمجية
01
الأساسيات
02
التحكم والدوال
03
البيانات المركبة
04
DOM & Events
05
ES6+ Modern
06
Asynchronous
07
OOP & Patterns
08
Performance
المشروع
// تقدمك
0% بداية الكورس
01
// UNIT ONE
الأساسيات — كيف تفكر JavaScript
قبل أي سطر كود، لازم تبني في دماغك صورة واضحة لـ "إيه اللي بيحصل فعلاً" لما المتصفح يقرأ JavaScript. ده الفرق بين حد بيحفظ syntax وحد فاهم البرمجة فعلاً.
4 دروس1 مطب برمجي1 تاسك~70 دقيقة
01
// FOUNDATION

ما هي JavaScript؟ ولماذا هي مختلفة؟

من لغة تزيين بسيطة إلى محرك الويب الحديث

HTML بتبني الهيكل. CSS بتضيف الشكل. لكن الاتنين دول static — يعني مش بيتغيروا من نفسهم. لو عايز الصفحة "تستجيب"، "تتفاعل"، "تتخذ قرار" — محتاج JavaScript.

JavaScript هي لغة برمجة حقيقية — مش لغة وصف زي HTML أو CSS. ده معناه عندها: متغيرات، شروط، حلقات، دوال، ومنطق حقيقي. هي اللغة الوحيدة اللي بتشتغل في كل متصفح على وجه الأرض بدون استثناء — ده اللي خلاها أهم لغة برمجة في تاريخ الإنترنت.

ظهرت JavaScript سنة 1995 على إيد Brendan Eich في شركة Netscape — وكُتبت في 10 أيام بس! اسمها الأصلي كان Mocha، بعدين LiveScript، وأخيراً JavaScript (اسم تسويقي، مالهاش علاقة فعلية بلغة Java).

النسخة الحالية المعيارية اسمها ECMAScript (ES). بتتطور كل سنة — ES6 (2015) كانت نقلة ضخمة، وإحنا دلوقتي على ES2026 اللي هنتعلمه بالكامل في الكورس ده.

JavaScript مش بس للمتصفح: أصلها كانت للمتصفح بس، لكن دلوقتي JavaScript بتشتغل في كل حتة — Node.js للـ backend servers، React Native للموبايل، Electron لتطبيقات Desktop (زي VS Code نفسه!). تعلمها هنا = باب لمسارات كتير قدامك.
02
// MENTAL MODEL

كيف يُنفّذ المتصفح كود JavaScript

أهم درس في الكورس كله — هتفهم إزاي JS فعلياً بتشتغل
⚡ Mental Model أساسي

المتصفح عنده محرك JavaScript (في Chrome اسمه V8). لما بتفتح صفحة فيها <script>، المحرك بيعمل الخطوات دي بالترتيب:

  1. Parsing — المحرك بيقرأ الكود ويحوّله لشجرة (AST) عشان يفهم بنيته
  2. Compilation — V8 بيحوّل الكود لـ bytecode سريع التنفيذ (Just-In-Time Compilation)
  3. Execution — الكود بيتنفذ سطر سطر، من فوق لتحت

JavaScript لغة Single-Threaded — يعني عندها "Thread" واحد بس بينفذ كود في وقت واحد. لو سطر كود بياخد وقت طويل (زي حلقة ضخمة)، الصفحة كلها بتتجمد لحد ما يخلص. ده مفهوم هيرجعلنا في وحدة Asynchronous JS لما نتعلم إزاي نتعامل مع عمليات بطيئة من غير ما نجمّد الصفحة.

أهم حاجة فاهمها دلوقتي: كل سطر JavaScript بينفذ بالترتيب، واحد بعد التاني. لو متغير اتعرّف في السطر 5، مش هتقدر تستخدمه في السطر 2 — لأن المحرك لسه ما وصلش لسطر 5 وقتها.

إزاي تربط JavaScript بالـ HTML؟ أفضل طريقة: حط <script src="script.js" defer></script> قبل </body> مباشرةً، أو في <head> مع attribute defer. الـ defer بيخلي المتصفح يحمّل الـ HTML كامل أولاً، وبعدين ينفذ الـ JS — كده مش هتحاول تتحكم في عنصر لسه ما اتعملش render.
index.html
<!DOCTYPE html>
<html>
<head>
  // الطريقة الموصى بها — defer
  <script src="script.js" defer></script>
</head>
<body>
  <h1 id="title">مرحباً</h1>
</body>
</html>
03
// VARIABLES

المتغيرات — let, const, var

أول أداة هتستخدمها في كل سطر كود تقريباً
⚡ استخدم const افتراضياً

المتغير هو "صندوق" بتحط فيه قيمة وتديله اسم عشان ترجعله. في JavaScript الحديثة 3 طرق لتعريف متغير — لكن طريقتين بس هتستخدمهم فعلياً.

variables.js
// ── const: قيمة ثابتة — استخدمها كافتراضي ──
// المتغير ده مش ممكن يتغير قيمته بعد التعريف
const userName = "Yahya";
const age = 25;

// ── let: قيمة ممكن تتغير ──
// استخدمها بس لو فعلاً متأكد إن القيمة هتتغير
let score = 0;
score = 10; // تمام — let بتسمح بالتغيير
score = score + 5; // score بقت 15

// ── var: القديمة — تجنبها تماماً ──
// موجودة من 1995، فيها مشاكل في الـ Scope (هنشرحها)
var oldWay = "تجنبها";

// ── محاولة تغيير const ──
const PI = 3.14;
PI = 3.15; // ❌ TypeError: Assignment to constant variable

القاعدة الذهبية: ابدأ دايماً بـ const. لو لقيت إن القيمة محتاجة تتغير فعلاً (زي عداد، أو حالة بتتبدل)، غيّرها لـ let. ده بيمنعك من أخطاء كتير — لو غيّرت قيمة كان المفروض ثابتة، JavaScript هتديك error فوراً بدل ما الباج يحصل بصمت.

💡
const مع Objects/Arrays

const بتمنع تغيير "المتغير نفسه" — لكن لو المتغير ده Object أو Array، تقدر تغيّر محتوياته! مثال: const arr = [1,2]; arr.push(3) — ده شغال، لأنك مش بتغيّر arr نفسه، بتغيّر محتواه. هنشرح ده بالتفصيل في وحدة Arrays.

04
// DATA TYPES

أنواع البيانات الأساسية (Primitives)

كل قيمة في JavaScript عندها نوع — وفهم الأنواع يمنعك أخطاء كتير

JavaScript عندها 7 أنواع بيانات أساسية (Primitives)، وفاهمين منهم 6 يكفوا تماماً للبداية:

النوعمثالالوصف
String"Yahya"نصوص — بين علامتي تنصيص أو backticks
Number25, 3.14, -10كل الأرقام — صحيحة وعشرية في نوع واحد
Booleantrue, falseقيمة منطقية — صح أو غلط فقط
Undefinedundefinedمتغير اتعرّف لكن مالوش قيمة
Nullnull"لا قيمة" — يتحط قصداً من المبرمج
SymbolSymbol()قيمة فريدة — استخدام متقدم نادر
data-types.js
// ── String: نصوص ──
const name = "Yahya";        // double quotes
const city = 'Cairo';        // single quotes — نفس الشيء

// ── Number: كل الأرقام نوع واحد ──
const age = 25;
const price = 19.99;
const temperature = -5;

// ── Boolean: صح أو غلط ──
const isLoggedIn = true;
const isAdmin = false;

// ── Undefined: ما اتحددش قيمة ──
let favoriteColor;
console.log(favoriteColor); // undefined

// ── Null: "لا شيء" قصداً ──
let selectedUser = null; // مفيش مستخدم متحدد حالياً

// ── typeof: معرفة نوع المتغير ──
console.log(typeof name);       // "string"
console.log(typeof age);        // "number"
console.log(typeof isLoggedIn); // "boolean"
Console Output
// undefined
"string"
"number"
"boolean"
undefined vs null — الفرق المهم: undefined معناها "المتغير اتعرّف لكن المتصفح مش عارف قيمته" — بيحصل تلقائياً. null معناها "أنا قصداً حطيت إن مفيش قيمة" — بتحطها انت بنفسك. مثال عملي: let selectedUser = null يعني "لسه محدش متحدد"، أما let x من غير قيمة يبقى undefined تلقائياً.
⚠️
مطبات برمجية شائعة — الوحدة الأولى

1. محاولة استخدام متغير قبل تعريفه

Uncaught ReferenceError: Cannot access 'score' before initialization
الحل: لازم تعرّف المتغير بـ let/const قبل ما تستخدمه. JavaScript بتنفذ بالترتيب من فوق لتحت — السبب إنه ReferenceError مش undefined هو إن let/const عندهم "Temporal Dead Zone" بيمنعوا استخدام المتغير قبل تعريفه تماماً، عكس var القديمة.

2. الخلط بين = و ==

// مفيش error — لكن النتيجة مش متوقعة! if (x = 5) { console.log("هيشتغل دايماً!"); }
الحل: = للتعيين (assignment)، == أو === للمقارنة. لو استخدمت = جوّا if قصداً غلط، الكود مش هيديك error — هيدي true دايماً لأنه بيعيّن القيمة 5 ويرجعها. ده من أخطر الأخطاء لأنه صامت.

3. محاولة تغيير const

Uncaught TypeError: Assignment to constant variable.
الحل: لو الـ error ده ظهر، يعني المتغير ده فعلياً محتاج يتغير — استبدل const بـ let. أو لو مش قصدك تغيّره، دور على مكان تاني في كودك بيحاول يعدّله بالغلط.
TASK 01
أنشئ ملف script.js وأول متغيرات للـ Portfolio
الوحدة الأولى · الأساسيات
  • أنشئ ملف script.js وربطه في الـ HTML بـ <script src="script.js" defer>
  • عرّف const لاسمك، مهنتك، وسنة بداية البرمجة
  • اطبعهم في Console بـ console.log() وافتح DevTools للتأكد
  • جرّب typeof على كل متغير وشوف النتيجة
  • جرّب قصداً تغيّر قيمة const وشوف الـ error في Console
  • جرّب الفرق بين null وundefined بكتابة console.log لكل واحد
  • اعمل console.table بدل console.log لمتغيرات متعددة وشوف الفرق
Portfolio Step 1 — الأساسالـ Portfolio بتاعك دلوقتي عنده ملف JS مربوط وجاهز. كل التفاعل الجاي هيتبني على الملف ده.
  • "Chrome DevTools Console tutorial" — تعلم كل إمكانيات الـ Console
  • "JavaScript defer vs async script" — الفرق المهم بينهم
UNIT 02
02
// UNIT TWO
التحكم بالتدفق والدوال
هنا بيبدأ "التفكير البرمجي" الحقيقي — إزاي تخلي الكود ياخد قرارات، يكرر عمليات، وينظّم نفسه في وحدات قابلة لإعادة الاستخدام.
5 دروس1 مطب برمجي1 تاسك~90 دقيقة
05
// OPERATORS

العمليات والمقارنات

الأدوات اللي بتقارن وتحسب وتتخذ قرارات
⚡ استخدم === دايماً
operators.js
// ── Arithmetic: العمليات الحسابية ──
const sum  = 10 + 5;  // 15 — جمع
const diff = 10 - 5;  // 5  — طرح
const prod = 10 * 5;  // 50 — ضرب
const quot = 10 / 5;  // 2  — قسمة
const rem  = 10 % 3;  // 1  — باقي القسمة (Modulo)
const power = 2 ** 3;  // 8 — أس (2 أس 3)

// ── Shorthand Operators ──
let counter = 0;
counter++;        // زيادة 1 (counter = counter + 1)
counter += 5;    // counter = counter + 5
counter -= 2;    // counter = counter - 2

// ── Comparison: المقارنة ──
// == بيقارن القيمة بس (مع تحويل النوع تلقائياً) ← تجنبه
console.log(5 == "5");   // true (يحول "5" لرقم!)

// === بيقارن القيمة والنوع مع بعض ← استخدمه دايماً
console.log(5 === "5");  // false (Number ≠ String)
console.log(5 === 5);    // true

console.log(10 > 5);   // true
console.log(10 <= 9);  // false
console.log(5 !== "5"); // true — "مش بتساوي تماماً"

// ── Logical: الربط المنطقي ──
const isAdult = true;
const hasID   = false;

console.log(isAdult && hasID); // false — لازم الاتنين true
console.log(isAdult || hasID); // true  — يكفي واحد true
console.log(!isAdult);          // false — عكس القيمة

ليه === أفضل من ==؟ الـ == بتعمل "Type Coercion" — يعني بتحاول تحوّل النوعين لنفس النوع قبل المقارنة، وده بيسبب نتائج غريبة زي: "" == false → true، أو null == undefined → true. الـ === بتقارن القيمة والنوع مع بعض، فالنتيجة دايماً متوقعة ومنطقية. استخدم === دايماً إلا لو عندك سبب قوي جداً.

06
// CONDITIONALS

الشروط — if / else if / else / switch

إزاي الكود "يقرر" إيه اللي يحصل بناءً على الموقف
conditionals.js
// ── if / else if / else ──
const score = 75;

if (score >= 90) {
  console.log("ممتاز");
} else if (score >= 70) {
  console.log("جيد جداً"); // ← هيطبع ده
} else if (score >= 50) {
  console.log("مقبول");
} else {
  console.log("راسب");
}
// JavaScript بتفحص الشروط بالترتيب — أول شرط true بيتنفذ ويتوقف

// ── Ternary Operator: اختصار for if/else بسيط ──
const age = 20;
const status = age >= 18 ? "بالغ" : "قاصر";
// condition ? value-if-true : value-if-false

// ── switch: بديل لـ if عند مقارنة قيمة واحدة بحالات كتير ──
const day = "Mon";

switch (day) {
  case "Sat":
  case "Sun":
    console.log("إجازة");
    break; // مهم جداً — بدونه الكود هيكمل للـ case الجاي
  case "Mon":
    console.log("أول يوم في الأسبوع"); // ← هيطبع ده
    break;
  default:
    console.log("يوم عادي");
}
⚠️
متنساش break في switch

لو نسيت break، الكود هيكمل ينفذ كل الـ cases اللي بعده تلقائياً (اسمها "Fall-through"). ده غلط شائع جداً — فحص دايماً كل case عندها break إلا لو قصدك فعلاً تستخدم fall-through (زي case "Sat" و"Sun" في المثال).

07
// LOOPS

الحلقات — for, while, for...of

تكرار عملية معينة بدل ما تكتبها يدوياً مرة مرة
⚡ for...of للمصفوفات
loops.js
// ── for: الحلقة الكلاسيكية ──
// for (البداية; الشرط; الزيادة)
for (let i = 0; i < 5; i++) {
  console.log(`التكرار رقم ${i}`);
}
// يطبع: 0, 1, 2, 3, 4 — يبدأ من 0 وينتهي قبل 5

// ── while: تكرار طالما الشرط صحيح ──
let count = 0;
while (count < 3) {
  console.log(`Count: ${count}`);
  count++; // لازم تزود count يدوياً — لو نسيت = infinite loop!
}

// ── for...of: أفضل طريقة للمرور على Array ──
const fruits = ["تفاح", "موز", "عنب"];

for (const fruit of fruits) {
  console.log(fruit);
}
// أنظف وأوضح من for العادية — مفيش index لازم تتعامل معاه

// ── for...in: المرور على خصائص Object ──
const person = { name: "Yahya", age: 25 };
for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}
// name: Yahya
// age: 25

// ── break و continue ──
for (let i = 0; i < 10; i++) {
  if (i === 3) continue; // يتخطى التكرار ده فقط
  if (i === 6) break;    // يوقف الحلقة كلها
  console.log(i);
}
// يطبع: 0, 1, 2, 4, 5
إمتى تستخدم إيه؟ for...of للمرور على Arrays (الأنظف). for العادية لما محتاج الـ index نفسه أو تتحكم في خطوات التكرار. while لما عدد مرات التكرار مش معروف مسبقاً (زي "كرر لحد ما تلاقي القيمة دي"). for...in نادراً — للـ Objects بس، ومش موصى بيها للـ Arrays.
08
// FUNCTIONS

الدوال — Function Declaration, Expression, Arrow

تنظيم الكود في وحدات قابلة لإعادة الاستخدام — أساس البرمجة الحقيقية
⚡ Arrow Functions الأكثر استخداماً

الدالة (Function) هي "آلة" بتاخد مدخلات (Parameters)، تعمل عملية معينة، وترجع نتيجة (Return Value). بدل ما تكرر نفس الكود 10 مرات، بتكتبه مرة واحدة في دالة وتناديها كل ما تحتاجه.

functions.js
// ── 1. Function Declaration: التعريف الكلاسيكي ──
function greet(name) {
  return `أهلاً، ${name}!`;
}
console.log(greet("Yahya")); // "أهلاً، Yahya!"

// ── 2. Function Expression: الدالة كقيمة لمتغير ──
const add = function(a, b) {
  return a + b;
};
console.log(add(3, 5)); // 8

// ── 3. Arrow Function: الأحدث والأكثر استخداماً ──
const multiply = (a, b) => {
  return a * b;
};

// لو سطر واحد بس — تقدر تختصر أكتر (implicit return)
const square = n => n * n;
console.log(square(5)); // 25 — مفيش return ولا {}

// ── Default Parameters: قيمة افتراضية لو مفيش argument ──
function createUser(name, role = "Member") {
  return `${name} - ${role}`;
}
console.log(createUser("Yahya"));          // "Yahya - Member"
console.log(createUser("Sara", "Admin")); // "Sara - Admin"

// ── دالة من غير return: بترجع undefined تلقائياً ──
function logMessage(msg) {
  console.log(msg); // بتطبع بس — مفيش return
}
const result = logMessage("Hi");
console.log(result); // undefined

Arrow Functions بقت الافتراضي في JS الحديثة لـ 3 أسباب: أقصر وأنظف، مفيهاش مشكلة الـ this المعقدة (هنشرحها في وحدة OOP)، ومناسبة جداً للدوال القصيرة جوّا map/filter/forEach. لكن لسه Function Declaration مفيدة لما عايز الدالة "تترفع" (Hoisting) وتتنادي قبل تعريفها في الكود.

09
// SCOPE & CLOSURES

Scope & Closures — المفهوم الأعمق في JavaScript

إزاي JavaScript "بتتذكر" المتغيرات — أساس الكثير من الـ patterns المتقدمة
⚡ مفهوم متقدم — خد وقتك

Scope ببساطة هو "المنطقة" اللي المتغير يقدر يتشاف فيها. في JavaScript الحديثة، كل { } بتعمل Scope جديد (Block Scope) — المتغيرات اللي اتعرّفت بـ let/const جواه مش متاحة برّا.

scope.js
// ── Block Scope ──
if (true) {
  const message = "جوّا الـ if";
  console.log(message); // شغال — جوّا نفس الـ Scope
}
console.log(message); // ❌ ReferenceError: message is not defined

// ── Function Scope ──
function calculate() {
  const result = 100;
  return result;
}
console.log(result); // ❌ result محبوسة جوّا الدالة فقط

// ════════════════════════════════════════
// ── Closure: دالة "بتتذكر" البيئة اللي اتعرّفت فيها ──
// ════════════════════════════════════════

function createCounter() {
  let count = 0; // متغير "خاص" — برّا مش تقدر توصله مباشرة

  return function() {
    count++; // الدالة الداخلية "بتتذكر" count حتى بعد ما createCounter خلصت
    return count;
  };
}

const counter1 = createCounter();
console.log(counter1()); // 1
console.log(counter1()); // 2 — بيفتكر القيمة القديمة!
console.log(counter1()); // 3

const counter2 = createCounter(); // counter منفصل تماماً
console.log(counter2()); // 1 — مش متأثر بـ counter1

إيه فايدة الـ Closures عملياً؟ بتسمحلك تعمل "متغيرات خاصة" (Private Variables) — count مش متاح من برّا الدالة، الطريقة الوحيدة تغيّره هي عن طريق الدالة اللي رجعتها createCounter. ده أساس الكثير من الـ patterns الاحترافية، وهتستخدمه كتير في الـ event handlers وفي الـ Module Pattern.

ℹ️
مش لازم تفهمها 100% دلوقتي

الـ Closures من أصعب المفاهيم في JS للمبتدئين. لو حسيت إنها مش واضحة، كمّل في الكورس وارجعلها بعدين — هتلاقيها بتتضح تلقائياً لما تشتغل على event listeners في وحدة DOM. الفهم بييجي بالممارسة مش بالحفظ.

⚠️
مطبات برمجية شائعة — الوحدة الثانية

1. Infinite Loop — حلقة بلا نهاية

Page Unresponsive — the page is slowing down your browser
الحل: دايماً تأكد إن متغير الحلقة بيتغير جوّا الـ loop نفسها. الغلطة الشائعة: while(count < 5) { console.log(count) } — نسيت count++ جوّا، فالشرط هيفضل true للأبد. لو حصل ده فعلياً، اقفل التاب أو اضغط Escape في Chrome.

2. مناداة دالة ناقصة Parameters

// مفيش error — لكن النتيجة فيها NaN function add(a, b) { return a + b; } add(5); // NaN — b بقت undefined
الحل: JavaScript مش بتجبرك تدي كل الـ parameters — اللي ناقص بيبقى undefined. استخدم Default Parameters (زي ما اتعلمنا) أو أضف validation جوّا الدالة نفسها للتأكد إن القيم موجودة.

3. استخدام متغير خارج الـ Scope بتاعه

Uncaught ReferenceError: count is not defined
الحل: لو عرّفت متغير بـ let/const جوّا { }، هو مش هيكون متاح برّا الـ { } دي. لو محتاجه في مكان تاني، عرّفه برّا الـ block من الأول، أو رجّعه (return) من الدالة لو هو جوّا function.
TASK 02
دوال منطقية للـ Portfolio
الوحدة الثانية · التحكم والدوال
  • اكتب دالة getGreeting() ترجع تحية مختلفة حسب الوقت (صباح/مساء) باستخدام if/else ودالة Date()
  • اكتب دالة calculateExperience(startYear) ترجع عدد سنين خبرتك من السنة اللي بدأت فيها
  • استخدم Arrow Function لدالة بسيطة تحول الاسم لحروف كبيرة (toUpperCase)
  • استخدم loop (for...of) لطباعة كل مهاراتك من Array في الـ Console
  • اعمل Closure بسيط: دالة createSkillCounter() بتعد كل مهارة بتضيفها
  • استخدم switch بدل if/else للتحية حسب اليوم (السبت إجازة، باقي الأيام شغل)
Portfolio Step 2 — المنطق الأساسيعندك دلوقتي دوال جاهزة هتستخدمها لاحقاً لما تربط الـ JS بالـ HTML فعلياً في وحدة DOM.
  • "JavaScript Date object methods" — getHours, getFullYear, إلخ
  • "JavaScript closures explained simply" — شرح بصري للمفهوم
UNIT 03
03
// UNIT THREE
البيانات المركبة — Arrays & Objects
لحد دلوقتي تعاملنا مع قيم منفردة. الحياة الحقيقية فيها بيانات معقدة — قائمة مشاريع، بيانات مستخدم، إعدادات. هنا هتتعلم إزاي تنظّم وتتلاعب بالبيانات دي باحترافية.
5 دروس1 مطب برمجي1 تاسك~95 دقيقة
10
// ARRAYS

Arrays والـ Methods الأساسية

قوائم مرتبة من البيانات — العمود الفقري لأي تطبيق حقيقي

الـ Array هي قائمة مرتبة من القيم — ممكن تحتوي على أي نوع بيانات، وحتى أنواع مختلطة. كل عنصر له "index" بيبدأ من 0.

arrays-basics.js
// ── إنشاء Array ──
const skills = ["HTML", "CSS", "JavaScript"];
// index:        0         1            2

// ── الوصول لعنصر ──
console.log(skills[0]); // "HTML"
console.log(skills[2]); // "JavaScript"
console.log(skills.length); // 3 — عدد العناصر

// ── إضافة وحذف عناصر ──
skills.push("React");     // إضافة في النهاية → ["HTML","CSS","JS","React"]
skills.pop();             // حذف آخر عنصر → يرجع "React" ويحذفه
skills.unshift("Git");  // إضافة في البداية → ["Git","HTML","CSS","JS"]
skills.shift();           // حذف أول عنصر

// ── البحث في Array ──
console.log(skills.includes("CSS")); // true
console.log(skills.indexOf("CSS"));  // 1 (مكانها)

// ── قص جزء من Array ──
const numbers = [1,2,3,4,5];
console.log(numbers.slice(1, 3)); // [2, 3] — من index 1 لحد 3 (مش شامل)

// ── ترتيب وعكس ──
const nums = [5, 2, 8, 1];
nums.sort((a, b) => a - b); // [1,2,5,8] — تصاعدي
nums.reverse();              // [8,5,2,1] — عكس الترتيب

// ── join: تحويل Array لنص ──
console.log(skills.join(", ")); // "HTML, CSS, JavaScript"
⚠️
sort() بترتب كنصوص افتراضياً!

[10, 2, 30].sort() بترجع [10, 2, 30] مش [2, 10, 30]! لأن sort بتقارن كنصوص ("10" أصغر من "2" أبجدياً). لازم تدّيها compare function: .sort((a,b) => a - b) للترتيب الرقمي الصحيح.

11
// HIGHER-ORDER

map, filter, reduce — أقوى أدوات في JavaScript

الانتقال من "حلقات يدوية" إلى "تفكير دالي" — قمة الاحترافية
⚡ أساسي لكل مبرمج محترف

دول 3 دوال بتاخد دالة تانية كـ argument وبتشتغل على كل عنصر في الـ Array. اسمهم "Higher-Order Functions". بدل ما تكتب for loop يدوي، بتستخدمهم وبيبقى الكود أوضح وأقصر.

map-filter-reduce.js
const projects = [
  { name: "Portfolio", category: "frontend", year: 2026 },
  { name: "API Server", category: "backend",  year: 2025 },
  { name: "Dashboard", category: "frontend", year: 2026 },
];

// ── map: تحويل كل عنصر لقيمة جديدة (نفس عدد العناصر) ──
const names = projects.map(project => project.name);
console.log(names); // ["Portfolio", "API Server", "Dashboard"]

// ── filter: استخراج عناصر مطابقة لشرط (عدد أقل أو يساوي) ──
const frontendProjects = projects.filter(p => p.category === "frontend");
console.log(frontendProjects); // [Portfolio, Dashboard]

// ── reduce: تجميع كل العناصر في قيمة واحدة ──
const cart = [{price:100}, {price:250}, {price:50}];
const total = cart.reduce((sum, item) => sum + item.price, 0);
//                              ↑accumulator  ↑current  ↑initial value
console.log(total); // 400

// ── دمج الثلاثة مع بعض — pattern احترافي شائع ──
const result = projects
  .filter(p => p.year === 2026)   // فلترة المشاريع الحديثة
  .map(p => p.name.toUpperCase()); // تحويل لأسماء كابيتال
console.log(result); // ["PORTFOLIO", "DASHBOARD"]

// ── find: أول عنصر مطابق فقط (مش Array) ──
const firstFrontend = projects.find(p => p.category === "frontend");
console.log(firstFrontend); // { name: "Portfolio", ... }

// ── some / every: فحص شرط على كل المصفوفة ──
console.log(projects.some(p => p.year === 2025));  // true — على الأقل واحد
console.log(projects.every(p => p.year >= 2025)); // true — كل العناصر

ليه دول أهم من for loop؟ أولاً: الكود بيوصف "إيه اللي عايز تعمله" مش "إزاي تعمله" — أوضح للقراءة. ثانياً: مفيهاش side effects — مش بتغير الـ Array الأصلي (إلا reduce لو قصدت). ثالثاً: قابلة للدمج (chaining) زي المثال اللي فات. دول هيبقوا أكتر حاجة بتستخدمها في الـ Project Filter بتاع الـ Portfolio لاحقاً.

12
// OBJECTS

Objects — إنشاء واستخدام الكائنات

تخزين بيانات مترابطة بأسماء واضحة بدل index رقمي
objects.js
// ── إنشاء Object ──
const developer = {
  name: "Yahya",
  title: "Full-Stack Developer",
  yearsExp: 3,
  skills: ["HTML", "CSS", "JS"],
  // methods — دالة جوّا object
  greet() {
    return `أنا ${this.name}`; // this تشير للـ object نفسه
  }
};

// ── الوصول للخصائص: طريقتين ──
console.log(developer.name);        // dot notation — الأكثر شيوعاً
console.log(developer["name"]);    // bracket notation — لو اسم الخاصية متغير

const key = "title";
console.log(developer[key]); // bracket مفيدة هنا — مش ممكن بـ dot

// ── إضافة وتعديل خصائص ──
developer.location = "Egypt";  // إضافة خاصية جديدة
developer.yearsExp = 4;      // تعديل قيمة موجودة
delete developer.location;  // حذف خاصية

// ── استدعاء method ──
console.log(developer.greet()); // "أنا Yahya"

// ── Object.keys / values / entries ──
console.log(Object.keys(developer));   // ["name","title","yearsExp","skills"]
console.log(Object.values(developer)); // ["Yahya","Full-Stack...",3,[...]]

// ── nested objects: object جوّا object ──
const portfolio = {
  owner: { name: "Yahya", contact: { email: "yahya@dev.com" } }
};
console.log(portfolio.owner.contact.email); // "yahya@dev.com"
13
// DESTRUCTURING

Destructuring — تفكيك البيانات

استخراج قيم من Array أو Object بسطر واحد أنيق
destructuring.js
// ── Array Destructuring ──
const colors = ["red", "green", "blue"];

// الطريقة القديمة:
const first = colors[0];
const second = colors[1];

// الطريقة الحديثة — أسطر واحد:
const [primary, secondary, tertiary] = colors;
console.log(primary); // "red"

// تخطي عنصر
const [, , third] = colors;
console.log(third); // "blue"

// ── Object Destructuring — أكثر استخداماً ──
const developer = { name: "Yahya", title: "Developer", age: 25 };

// الطريقة القديمة:
const n = developer.name;
const t = developer.title;

// الطريقة الحديثة — أسماء الخصائص بالظبط:
const { name, title } = developer;
console.log(name); // "Yahya"

// إعادة تسمية أثناء الـ destructuring
const { name: devName } = developer;
console.log(devName); // "Yahya"

// قيمة افتراضية لو الخاصية مش موجودة
const { location = "غير محدد" } = developer;
console.log(location); // "غير محدد"

// ── Destructuring في Parameters — شائع جداً ──
function displayUser({ name, title }) {
  return `${name} - ${title}`;
}
console.log(displayUser(developer)); // "Yahya - Developer"
14
// SPREAD & REST

Spread & Rest Operators (...)

نفس الرمز، استخدامين مختلفين — توسيع البيانات أو تجميعها
spread-rest.js
// ── Spread: "تفريد" عناصر Array أو Object ──

// نسخ Array بدون التأثير على الأصلي
const original = [1, 2, 3];
const copy = [...original]; // [1,2,3] — نسخة جديدة منفصلة

// دمج Arrays
const frontend = ["HTML", "CSS"];
const backend  = ["Node", "Express"];
const fullStack = [...frontend, ...backend];
// ["HTML","CSS","Node","Express"]

// إضافة عنصر بدون تعديل الأصلي (Immutable pattern)
const withNew = [...frontend, "JavaScript"];
// ["HTML","CSS","JavaScript"] — frontend الأصلي لم يتغير

// نسخ ودمج Objects
const baseConfig = { theme: "dark", lang: "ar" };
const userConfig = { ...baseConfig, theme: "light" };
// { theme: "light", lang: "ar" } — الخاصية المكررة بتاخد آخر قيمة

// ── Rest: تجميع عناصر متبقية في Array واحد ──

// في Function Parameters — عدد غير محدد من المدخلات
function sum(...numbers) {
  return numbers.reduce((total, n) => total + n, 0);
}
console.log(sum(1, 2, 3, 4)); // 10 — يقبل أي عدد من الأرقام

// مع Destructuring
const [first, ...rest] = [10, 20, 30, 40];
console.log(first); // 10
console.log(rest);  // [20, 30, 40]
ازاي تفرّق بين Spread وRest؟ القاعدة بسيطة: لو ... بتظهر في جهة اليمين (داخل [] أو {} عند الإنشاء) فهي Spread — بتفك العناصر. لو ظاهرة في جهة الـ Parameters أو في Destructuring، فهي Rest — بتجمع العناصر المتبقية. الرمز نفسه، السياق هو اللي بيحدد الوظيفة.
⚠️
مطبات برمجية شائعة — الوحدة الثالثة

1. تعديل Array أو Object بالخطأ (Mutation)

// مفيش error — لكن بيغيّر الـ original بدون قصد! const original = [1, 2, 3]; const modified = original; // ده مش نسخة! ده نفس الـ array modified.push(4); console.log(original); // [1, 2, 3, 4] ← اتغيرت!
الحل: Arrays وObjects بتتنسخ "بالمرجع" (by reference) مش بالقيمة. لو عايز نسخة حقيقية مستقلة، استخدم Spread: const modified = [...original]. ده مهم جداً ومن أكتر مصادر الأخطاء صعبة الاكتشاف في JavaScript.

2. محاولة الوصول لخاصية في Object غير موجود

Uncaught TypeError: Cannot read properties of undefined (reading 'email')
الحل: ده بيحصل لما تحاول توصل لـ nested property وواحد من المستويات مش موجود، زي user.contact.email وuser.contact أصلاً undefined. الحل: Optional Chaining user.contact?.email — هنشرحها بالتفصيل في وحدة ES6+.

3. استخدام map بدل forEach (أو العكس)

// مش error — لكن استهلاك ذاكرة غير ضروري const result = items.map(item => console.log(item)); // map بترجع array جديد لكل عنصر undefined!
الحل: استخدم map لما عايز array جديد من القيم المُرجعة. استخدم forEach لما بس عايز تنفذ عملية (زي console.log) بدون الحاجة لقيمة مُرجعة. الخلط بينهم شائع عند المبتدئين.
TASK 03
بنية بيانات المشاريع للـ Portfolio
الوحدة الثالثة · البيانات المركبة
  • أنشئ Array من Objects لمشاريعك: كل project فيه name, category, description, technologies (array)
  • استخدم filter() لاستخراج المشاريع من category معينة فقط
  • استخدم map() لاستخراج أسماء كل المشاريع في array منفصل
  • استخدم Destructuring لاستخراج name وtechnologies من أول مشروع
  • استخدم Spread لعمل نسخة من الـ projects array وإضافة مشروع جديد بدون التأثير على الأصلي
  • استخدم reduce() لحساب إجمالي عدد التقنيات المستخدمة عبر كل المشاريع
  • رتّب المشاريع حسب السنة باستخدام sort()
Portfolio Step 3 — بنية البياناتدلوقتي عندك بنية بيانات حقيقية لمشاريعك. ده الأساس اللي هتبني عليه الـ Project Filter التفاعلي في وحدة DOM.
  • "JavaScript array methods cheat sheet" — مرجع سريع لكل الـ methods
  • "shallow copy vs deep copy JavaScript" — مفهوم مهم للمستقبل
UNIT 04
04
// UNIT FOUR · الأهم في الكورس
DOM & Events — هنا JavaScript بتحرّك صفحتك فعلياً
كل اللي اتعلمته لحد دلوقتي كان جوّا Console بس. هنا هتتعلم إزاي توصل JavaScript بالـ HTML الحقيقي وتخليه يتفاعل مع المستخدم — ده قلب الـ Frontend Development.
5 دروس1 مطب برمجي1 تاسك~110 دقيقة
15
// MENTAL MODEL

DOM Mental Model — الشجرة اللي المتصفح بيبنيها

أهم Mental Model في الفرونت إند كله — افهمه كويس قبل ما تكمل
⚡ توقف هنا واستوعب

لما المتصفح يقرأ ملف HTML، هو مش بيشتغل على النص نفسه — بيحوّله لبنية بيانات في الذاكرة اسمها DOM (Document Object Model). الـ DOM دي عبارة عن شجرة — كل tag في HTML بيتحول لـ "node" في الشجرة دي.

فكّر في الـ HTML بتاعك كـ "مخطط بناء"، والـ DOM هو "البيت الفعلي اللي اتبنى". JavaScript مش بتتعامل مع ملف الـ HTML — هي بتتعامل مع الـ DOM، اللي هو تمثيل حي وقابل للتعديل للصفحة في ذاكرة المتصفح. لو غيّرت الـ DOM، الصفحة بتتغير فوراً قدام عينك — حتى لو ملف الـ HTML الأصلي ما اتغيرش.

مثال: الـ HTML ده:

example.html
<body>
  <header>
    <h1>Yahya.DEV</h1>
  </header>
  <section>
    <p>مرحباً</p>
  </section>
</body>

بيتحول لشجرة DOM شكلها كده:

DOM Tree
body
├── header
│    └── h1 ("Yahya.DEV")
└── section
       └── p ("مرحباً")

كل "صندوق" في الشجرة دي اسمه Element Node. والعلاقات بينهم لها أسماء محددة هتستخدمها كتير:

  • Parent — العنصر الأب (header هي parent للـ h1)
  • Child — العنصر الابن (h1 هي child لـ header)
  • Sibling — عناصر في نفس المستوى (header وsection أخوات)
ليه ده مهم عملياً؟ JavaScript هتستخدم العلاقات دي للتنقل في الصفحة — مثلاً "اختار كل children بتاعة العنصر ده" أو "روح للـ parent بتاع الزرار اللي اتدوس". فهم الشجرة = فهم إزاي تتحرك جواها بسهولة.
16
// SELECTING

اختيار وتعديل العناصر

إزاي توصل لأي عنصر في الصفحة وتغيّر فيه
⚡ querySelector هو الأهم
selecting.js
// ── querySelector: اختيار أول عنصر مطابق ──
// نفس syntax الـ CSS Selectors اللي اتعلمتها
const heading     = document.querySelector("h1");          // أول h1
const heroSection = document.querySelector("#hero");       // بالـ id
const firstCard   = document.querySelector(".card");        // بالـ class — أول واحد
const navLink     = document.querySelector("nav a");        // descendant selector

// ── querySelectorAll: اختيار كل العناصر المطابقة ──
const allCards = document.querySelectorAll(".card");
console.log(allCards.length); // عدد العناصر اللي اتلاقت
// allCards بترجع NodeList — زي Array بس مش بالظبط
// تقدر تستخدم forEach عليها مباشرة
allCards.forEach(card => console.log(card));

// ── تعديل المحتوى ──
heading.textContent = "عنوان جديد";     // نص فقط — آمن
heading.innerHTML   = "<strong>Yahya</strong>"; // يقبل HTML tags

// ── تعديل الـ Styles مباشرة ──
heading.style.color = "#f0c808";
heading.style.fontSize = "3rem"; // camelCase بدل font-size

// ── تعديل Classes — الطريقة المفضلة احترافياً ──
heading.classList.add("active");     // إضافة class
heading.classList.remove("hidden");  // حذف class
heading.classList.toggle("open");    // لو موجود يشيله، لو مش موجود يضيفه
console.log(heading.classList.contains("active")); // true/false

// ── تعديل Attributes ──
const img = document.querySelector("img");
img.setAttribute("src", "new-image.jpg");
console.log(img.getAttribute("alt"));
🔐
innerHTML خطر أمني محتمل

لو حطيت في innerHTML نص جاي من المستخدم مباشرةً (زي تعليق أو input)، ممكن حد يحط <script> ضار جوّاه (XSS Attack). استخدم textContent دايماً لما بتتعامل مع نص بس، وinnerHTML بس لما واثق من مصدر الـ HTML.

classList بدل تعديل style مباشرة: الأفضل احترافياً إنك تعرّف الـ styles كلها في CSS classes، وJavaScript بس بتضيف وتشيل الـ classes دي. ده بيخلي الكود منظم — CSS مسؤولة عن "الشكل"، JS مسؤولة عن "السلوك" — مفيش خلط بينهم.
17
// CREATING ELEMENTS

إنشاء وحذف عناصر ديناميكياً

بناء HTML بالكامل من JavaScript — أساس أي تطبيق ديناميكي
creating-elements.js
// ── إنشاء عنصر جديد من الصفر ──
const newCard = document.createElement("div");
newCard.classList.add("project-card");
newCard.textContent = "مشروع جديد";

// ── إضافته للصفحة ──
const container = document.querySelector("#projects");
container.appendChild(newCard); // يضيفه في الآخر
// أو الطريقة الأحدث:
container.append(newCard);     // مرنة أكتر — تقبل نص وعدة عناصر

// ── بناء card كاملة بـ HTML structure ──
function createProjectCard(project) {
  const card = document.createElement("article");
  card.classList.add("project-card");

  // أفضل طريقة: بناء العناصر منفصلة ثم دمجها
  const title = document.createElement("h3");
  title.textContent = project.name;

  const desc = document.createElement("p");
  desc.textContent = project.description;

  card.append(title, desc); // إضافة عناصر متعددة مرة واحدة
  return card;
}

// ── استخدامها على array من المشاريع ──
const projects = [
  { name: "Portfolio", description: "صفحتي الشخصية" },
  { name: "Todo App", description: "تطبيق مهام" }
];

projects.forEach(project => {
  const card = createProjectCard(project);
  container.append(card);
});

// ── حذف عناصر ──
newCard.remove(); // الطريقة الحديثة — مباشرة وبسيطة

// ── إفراغ container من كل محتواه قبل إعادة الملء ──
container.innerHTML = ""; // شائعة، لكن أبطأ من remove لكل عنصر
⚠️
تجنب استخدام innerHTML += داخل loop

كتابة container.innerHTML += "..." جوّا forEach بطيئة جداً للأداء — لأن المتصفح بيعيد بناء كل الـ DOM من جديد في كل تكرار. الأفضل: ابني العناصر بـ createElement وappend مرة واحدة، أو جمّع الـ HTML في string وحطه مرة واحدة في الآخر.

18
// EVENT LOOP

Event Listeners والـ Event Loop

إزاي JavaScript "بتستمع" لتفاعل المستخدم وتستجيب فوراً
⚡ قلب التفاعلية

لحد دلوقتي كل الكود بتاعنا كان بينفذ مرة واحدة من فوق لتحت. لكن المواقع الحقيقية مش كده — لازم تستجيب لما المستخدم يضغط زرار، يكتب، يعمل scroll. هنا بييجي دور الـ Events.

events.js
// ── addEventListener: الطريقة الصحيحة والوحيدة احترافياً ──
const button = document.querySelector("#submitBtn");

button.addEventListener("click", function() {
  console.log("تم الضغط!");
});

// ── الوصول لمعلومات الـ Event نفسه ──
button.addEventListener("click", event => {
  console.log(event.target);       // العنصر اللي اتدوس عليه
  event.preventDefault();         // يمنع السلوك الافتراضي (مهم في forms)
});

// ── أشهر أنواع الـ Events ──
button.addEventListener("click", handleClick);
input.addEventListener("input", handleTyping);   // كل ما المستخدم يكتب حرف
form.addEventListener("submit", handleSubmit);   // إرسال الفورم
window.addEventListener("scroll", handleScroll); // التمرير
window.addEventListener("load", handleLoad);     // لما الصفحة تخلص تحميل
input.addEventListener("focus", handleFocus);   // لما الـ input يتفعّل
input.addEventListener("blur", handleBlur);     // لما يفقد الفوكس

// ── مثال عملي: Dark Mode Toggle ──
const themeToggle = document.querySelector("#themeToggle");

themeToggle.addEventListener("click", () => {
  document.documentElement.classList.toggle("light-mode");
});

الـ Event Loop — إزاي JS بتستمع لكل حاجة في وقت واحد رغم إنها Single-Threaded؟ المتصفح عنده "Call Stack" بتنفذ الكود، و"Web APIs" بتستقبل الـ events (زي click وscroll) من برّا، و"Callback Queue" بتستنى فيها الـ event handlers لحد ما الـ Call Stack تفضى. الـ Event Loop بتراقب باستمرار: "الـ Stack فاضية؟ يبقى هات أول حاجة من الـ Queue ونفذها". كده JS بتحس إنها "بتستمع" لكل حاجة، رغم إنها فعلياً بتنفذ سطر واحد في كل لحظة.

💡
ليه addEventListener أحسن من onclick="..."؟

onclick في الـ HTML بيخلط المنطق بالهيكل (نفس مشكلة inline CSS). addEventListener بيخليك تفصل JS تماماً، وتقدر تضيف أكتر من listener على نفس العنصر، وتشيله بسهولة بـ removeEventListener وقت ما تحتاج.

19
// BUBBLING & DELEGATION

Event Bubbling & Delegation

المفهوم المتقدم اللي بيخليك تتحكم في مئات العناصر بـ listener واحد
⚡ Performance حقيقي

لما تضغط على عنصر جوّا عنصر تاني، الـ Event مش بتتفعل على العنصر ده بس — هي "بتطلع" (bubble) لكل الـ parents بتاعته كمان. ده اسمه Event Bubbling.

event-delegation.js
// ── المشكلة: عايز listener على كل li جوّا قائمة ──
// الطريقة الساذجة — أداء سيء وغير عملية:
const items = document.querySelectorAll("li");
items.forEach(item => {
  item.addEventListener("click", handleClick);
});
// المشكلة: لو أضفت li جديد بعدين، مش هيكون عنده listener!

// ── الحل الاحترافي: Event Delegation ──
// حط listener واحد بس على الـ parent، واستفد من الـ Bubbling
const list = document.querySelector("#projectsList");

list.addEventListener("click", event => {
  // event.target هو العنصر الفعلي اللي اتدوس عليه
  if (event.target.tagName === "LI") {
    console.log(`دوست على: ${event.target.textContent}`);
  }
});
// ✓ بيشتغل حتى مع عناصر تتضاف ديناميكياً بعدين
// ✓ listener واحد بدل مئات — أداء أفضل بكتير

// ── closest(): إيجاد أقرب parent مطابق ──
// مفيدة لو الكليك حصل على عنصر جوّا li (زي icon)
list.addEventListener("click", event => {
  const clickedItem = event.target.closest("li");
  if (clickedItem) {
    clickedItem.classList.toggle("selected");
  }
});

// ── إيقاف الـ Bubbling لو احتجت ──
button.addEventListener("click", event => {
  event.stopPropagation(); // يمنع الـ Event من الطلوع للـ parents
});
Event Delegation هتستخدمها في Project Filter: بدل ما تحط listener على كل project card لوحدها، هتحط واحد بس على الـ container الكلي. ده مهم جداً لو عندك مشاريع بتتضاف ديناميكياً من JavaScript — الـ delegation بتشتغل عليهم تلقائياً من غير ما تحتاج تضيف listener جديد كل مرة.
⚠️
مطبات برمجية شائعة — الوحدة الرابعة (الأخطر في الكورس)

1. محاولة الوصول لعنصر قبل ما الصفحة تحمّل

Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
الحل: ده أشهر error في DOM manipulation. السبب: السكريبت بيتنفذ قبل ما العنصر يتعمله render في الـ HTML، فـ querySelector بترجع null. الحل: حط <script defer> (زي ما اتعلمنا في Unit 1) أو حط السكريبت في آخر الـ body بعد كل الـ HTML.

2. النسيان إن querySelectorAll بترجع NodeList مش Array حقيقي

Uncaught TypeError: cards.map is not a function
الحل: NodeList عندها forEach لكن مش كل methods الـ Array (زي map وfilter). لو محتاج تستخدمهم، حول الـ NodeList لـ Array الأول: const cardsArray = Array.from(cards) أو [...cards].

3. تكرار إضافة Event Listeners

// مفيش error — لكن الدالة بتتنفذ مرتين أو أكتر! button.addEventListener("click", handleClick); button.addEventListener("click", handleClick); // نفس الـ listener اتضاف تاني!
الحل: لو الكود ده بيتنفذ أكتر من مرة (مثلاً جوّا دالة بتتنادى كتير)، كل مرة بيضيف listener جديد فوق القديم. الحل: تأكد إن إضافة الـ listener بتحصل مرة واحدة بس، أو استخدم removeEventListener قبل إضافة واحد جديد.

4. this جوّا Arrow Function في Event Listener

// this مش بيشير للعنصر زي المتوقع button.addEventListener("click", () => { console.log(this); // window, مش الـ button! });
الحل: Arrow Functions ما عندهاش this خاص بيها — بتاخده من الـ scope اللي اتعرّفت فيه. لو محتاج this يشير للعنصر اللي اتدوس عليه، استخدم function عادية أو استخدم event.currentTarget بدل this. هنشرح this بالتفصيل في وحدة OOP.
TASK 04
حوّل الـ Portfolio لصفحة تفاعلية
الوحدة الرابعة · DOM & Events
  • اعمل Hamburger Menu: زرار يضيف class "open" للـ nav menu عند الضغط (toggle)
  • اعمل Project Filter: أزرار categories (All, Frontend, Backend) بتفلتر الـ project cards باستخدام classList
  • استخدم createElement لبناء project cards من الـ Array اللي عملته في Task 3 وعرضها ديناميكياً
  • استخدم Event Delegation على الـ project container بدل listener لكل card
  • أضف smooth scroll للـ nav links باستخدام scrollIntoView()
  • اعمل "Back to Top" button يظهر بس لما scroll يتجاوز معين
Portfolio Step 4 — التفاعل الحقيقيالـ Portfolio بتاعك دلوقتي مش static — بيستجيب فعلياً للمستخدم. ده أكبر قفزة في الكورس كله.
  • "JavaScript event delegation explained" — فيديوهات توضيحية بصرية
  • "MDN scrollIntoView" — للـ smooth scroll الاحترافي
  • "Chrome DevTools Elements panel debugging"
UNIT 05
05
// UNIT FIVE
ES6+ Modern JavaScript
المميزات اللي بتفرّق بين كود قديم وكود 2026 احترافي. هتتعلم الأدوات اللي كل مبرمج محترف بيستخدمها يومياً وتتجنب الكود "العتيق" تماماً.
4 دروس1 تاسك~70 دقيقة
20
// TEMPLATE LITERALS

Template Literals — النصوص الذكية

وداعاً لجمع النصوص بـ + — استخدمتها فعلياً طول الكورس بدون ما نشرحها رسمياً

كنت شايف `${variable}` في كل الدروس اللي فاتت — دي Template Literals، وهنشرحها بالتفصيل دلوقتي.

template-literals.js
const name = "Yahya";
const age = 25;

// ── الطريقة القديمة: جمع نصوص بـ + ──
const oldWay = "اسمي " + name + " وعمري " + age;
// مزعجة، صعبة القراءة، سهلة الغلط في المسافات

// ── الطريقة الحديثة: Template Literals بـ backticks ──
const newWay = `اسمي ${name} وعمري ${age}`;
// واضحة، أسهل، تقدر تحط أي expression جوّا ${}

// ── تقدر تحط expressions كاملة مش بس متغيرات ──
const message = `بعد سنتين هتبقى عمرك ${age + 2}`;
const status = `أنت ${age >= 18 ? "بالغ" : "قاصر"}`;

// ── Multi-line Strings — أسطر متعددة بدون \n ──
const card = `
  <div class="card">
    <h3>${name}</h3>
    <p>العمر: ${age}</p>
  </div>
`;
// مفيدة جداً لبناء HTML structures كاملة من JS

// ── استخدام عملي: بناء project card ──
function renderProject(project) {
  return `
    <article class="project-card">
      <h3>${project.name}</h3>
      <p>${project.description}</p>
    </article>
  `;
}
21
// MODERN SAFETY

Optional Chaining (?.) & Nullish Coalescing (??)

حل نهائي للـ "Cannot read properties of undefined" اللي شفناها في المطبات
⚡ يحل error شائع جداً
optional-chaining.js
const user = {
  name: "Yahya",
  social: {
    github: "yahya-dev"
    // لاحظ: مفيش instagram هنا
  }
};

// ── المشكلة بدون Optional Chaining ──
console.log(user.social.instagram.followers);
// ❌ Uncaught TypeError: Cannot read properties of undefined
// لأن instagram أصلاً undefined، فمحاولة الوصول لـ .followers بتطيح

// ── الحل: Optional Chaining ?. ──
console.log(user.social?.instagram?.followers);
// undefined — مفيش error! بتوقف بهدوء لو أي خطوة undefined

// ── استخدامها مع Methods ──
const obj = {};
obj.someMethod?.(); // لو الـ method مش موجودة، مش هتطيح error

// ── استخدامها مع Arrays ──
const data = { items: null };
console.log(data.items?.[0]); // undefined — مش error

// ════════════════════════════════════
// ── Nullish Coalescing: قيمة بديلة افتراضية ──
// ════════════════════════════════════

const username = null;

// المشكلة باستخدام || (OR العادي):
const display1 = username || "زائر";
// لو username = 0 أو "" (نص فارغ)، برضو هيستخدم "زائر"! ده غلط أحياناً

// الحل: ?? بتفحص null أو undefined فقط، مش كل "falsy values"
const display2 = username ?? "زائر";
console.log(display2); // "زائر" — username فعلاً null

const score = 0;
console.log(score || 100); // 100 ← خطأ! score=0 لسه قيمة صحيحة
console.log(score ?? 100); // 0 ← صح! score مش null ولا undefined

دول من أهم إضافات JS الحديثة: Optional Chaining (ES2020) وNullish Coalescing (ES2020) بيحلوا مشكلتين شائعتين جداً: الخوف من undefined properties، والخلط بين "مفيش قيمة" و"القيمة صفر/فاضية". استخدمهم دايماً بدل الفحص اليدوي الطويل بـ if statements متعددة.

22
// MAP & SET

Map & Set — هياكل بيانات متقدمة

بدائل أقوى من Object وArray في حالات معينة
map-set.js
// ── Set: مجموعة بدون عناصر مكررة ──
const numbers = [1, 2, 2, 3, 3, 3];
const uniqueNumbers = [...new Set(numbers)];
console.log(uniqueNumbers); // [1, 2, 3] — حذف التكرارات تلقائياً!

const tags = new Set();
tags.add("javascript");
tags.add("frontend");
tags.add("javascript"); // مش هيتضاف — موجود بالفعل
console.log(tags.size); // 2
console.log(tags.has("frontend")); // true

// ── Map: زي Object بس بميزات أقوى ──
const userRoles = new Map();
userRoles.set("Yahya", "Admin");
userRoles.set("Sara", "Member");

console.log(userRoles.get("Yahya")); // "Admin"
console.log(userRoles.size);          // 2

// المرور على كل عناصر الـ Map
userRoles.forEach((role, user) => {
  console.log(`${user}: ${role}`);
});
ℹ️
إمتى تستخدم Map بدل Object؟

Map أفضل لما تحتاج: مفاتيح من أي نوع (مش بس نصوص)، ترتيب مضمون للعناصر، عدد كبير من القراءة/الكتابة المتكررة (أداء أفضل). Object يفضل أفضل في حالات بسيطة وأكتر شيوعاً. للمبتدئين: استخدم Object كافتراضي، وارجع لـ Map لو احتجت ميزاته تحديداً.

23
// MODULES

Modules — import / export

تقسيم الكود لملفات منظمة بدل ملف JS واحد ضخم

زي ما قسّمنا CSS لملفات منفصلة، JavaScript الحديثة بتسمحلك تقسّم الكود لـ modules — كل ملف مسؤول عن حاجة معينة، وتقدر تستوردها في ملفات تانية.

modules.js
// ════════════════════════════════════
// ملف: utils.js
// ════════════════════════════════════

// ── Named Export: تصدير أكتر من حاجة بالاسم ──
export function formatDate(date) {
  return date.toLocaleDateString("ar-EG");
}

export const SITE_NAME = "Yahya.DEV";

// ── Default Export: حاجة واحدة رئيسية للملف ──
export default function initApp() {
  console.log("التطبيق بدأ");
}

// ════════════════════════════════════
// ملف: main.js
// ════════════════════════════════════

// استيراد named exports بأسمائهم بالظبط
import { formatDate, SITE_NAME } from "./utils.js";

// استيراد default export — تقدر تسميه أي اسم
import initApp from "./utils.js";

console.log(SITE_NAME);          // "Yahya.DEV"
console.log(formatDate(new Date())); // تاريخ منسّق
initApp();

// ════════════════════════════════════
// ملف: index.html — لازم type="module"
// ════════════════════════════════════
// <script src="main.js" type="module"></script>
⚠️
Modules محتاجة سيرفر — مش بتشتغل بفتح الملف مباشرة

لو فتحت الـ HTML مباشرة بـ file:// في المتصفح، الـ Modules هتدّيك CORS error. لازم تشغّل الملف عن طريق local server (زي Live Server extension في VS Code) عشان الـ import/export تشتغل صح.

TASK 05
حسّن كود الـ Portfolio بمعايير ES6+
الوحدة الخامسة · ES6+ Modern JS
  • راجع دالة renderProject بتاعتك واستخدم Template Literals بدل أي جمع نصوص بـ +
  • استخدم Optional Chaining في أي مكان بتوصل فيه لـ nested properties من بيانات المشاريع
  • استخدم Set لاستخراج كل التقنيات الفريدة (بدون تكرار) المستخدمة في مشاريعك
  • قسّم الكود لملفين: utils.js (دوال مساعدة) وmain.js (المنطق الرئيسي) باستخدام import/export
  • استخدم Map لتخزين عدد المشاريع في كل category
  • استخدم Nullish Coalescing لقيم افتراضية لو بيانات مشروع ناقصة
Portfolio Step 5 — كود احترافيالكود بتاعك دلوقتي بمعايير 2026 الحديثة — منظم، آمن من الأخطاء الشائعة، وسهل القراءة.
  • "JavaScript ES6 features overview" — قائمة شاملة بكل المميزات الحديثة
  • "VS Code Live Server extension" — لتشغيل Modules محلياً
UNIT 06
06
// UNIT SIX · من أصعب وأهم الوحدات
Asynchronous JavaScript
العالم الحقيقي فيه عمليات بتاخد وقت — تحميل بيانات من سيرفر، انتظار رد API. هنا هتتعلم إزاي JavaScript بتتعامل مع البطء من غير ما تجمّد الصفحة.
5 دروس1 مطب برمجي1 تاسك~110 دقيقة
24
// MENTAL MODEL

Synchronous vs Asynchronous

الفرق الجوهري اللي هيغيّر فهمك لكل كود JS بعد كده
⚡ Mental Model حاسم

كل كود كتبناه لحد دلوقتي كان Synchronous — يعني بينفذ سطر سطر، كل سطر بينتظر اللي قبله يخلص. لكن بعض العمليات بطيئة طبيعياً (تحميل صورة، طلب بيانات من سيرفر) — لو خلّينا JS تستنى ليهم بشكل sync، الصفحة كلها هتتجمد.

sync-vs-async.js
// ── Synchronous: كل سطر بيستنى اللي قبله ──
console.log("1");
console.log("2");
console.log("3");
// النتيجة: 1, 2, 3 — بالترتيب بالظبط

// ── Asynchronous: setTimeout كمثال بسيط ──
console.log("1");

setTimeout(() => {
  console.log("2 (بعد ثانيتين)");
}, 2000); // 2000 ميلي ثانية = ثانيتين

console.log("3");

// النتيجة: 1, 3, 2 (بعد ثانيتين) ← مش 1,2,3!
// JavaScript ما استنتش الـ setTimeout — كملت تنفيذ سطر "3"
// وبعدين لما الوقت يخلص، نفّذت الـ callback

ليه ده مهم؟ لو عملية زي fetch بيانات من سيرفر كانت Synchronous، الصفحة هتتجمد تماماً (المستخدم مش هيقدر يضغط ولا يscroll) لحد ما الرد يوصل — وده ممكن ياخد ثواني. بفضل الـ Asynchronous، JavaScript بتكمل تنفيذ باقي الكود وتتعامل مع رد السيرفر "لما يجهز" من غير ما توقف حاجة تانية.

رحلة هذه الوحدة: هنشرح 3 طرق للتعامل مع Async — Callbacks (القديمة، فيها مشاكل)، Promises (الأفضل)، Async/Await (الأحدث والأوضح). كل طريقة بنيت فوق اللي قبلها، فهمهم بالترتيب هيخليك تقدّر ليه Async/Await بقت المعيار.
25
// CALLBACKS

Callbacks ومشكلة "Callback Hell"

الطريقة القديمة — لازم تفهمها عشان تقدّر الحل الأحدث
callbacks.js
// ── Callback: دالة بتتمرر كـ argument لتنفذ لاحقاً ──
function fetchUserData(userId, callback) {
  setTimeout(() => {
    const userData = { id: userId, name: "Yahya" };
    callback(userData); // تنفيذ الـ callback لما البيانات تجهز
  }, 1000);
}

fetchUserData(1, userData => {
  console.log(userData); // بعد ثانية: { id: 1, name: "Yahya" }
});

// ════════════════════════════════════
// ── المشكلة: Callback Hell ──
// لما عندك عمليات متتالية، كل واحدة محتاجة نتيجة اللي قبلها
// ════════════════════════════════════

fetchUser(1, user => {
  fetchPosts(user.id, posts => {
    fetchComments(posts[0].id, comments => {
      fetchLikes(comments[0].id, likes => {
        console.log(likes);
        // 😱 "هرم الموت" — كل سطر متداخل أعمق وأعمق
        // صعب القراءة، صعب الصيانة، صعب الـ error handling
      });
    });
  });
});
🔥
Callback Hell مشكلة حقيقية

كل ما العمليات المتسلسلة تزيد، الكود بيتداخل أعمق وأعمق ("Pyramid of Doom"). صعب تتبع الأخطاء، صعب تضيف منطق جديد، صعب تقرأ الترتيب. ده بالظبط اللي خلّى JavaScript تطوّر Promises كحل.

26
// PROMISES

Promises بعمق — الحل الأنيق

"وعد" بقيمة هتجي لاحقاً — إما تنجح أو تفشل
⚡ أساس Async/Await

الـ Promise هي object بتمثل عملية async هتخلص لاحقاً. عندها 3 حالات: pending (لسه شغالة)، fulfilled (نجحت)، rejected (فشلت).

promises.js
// ── إنشاء Promise ──
function fetchUserData(userId) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (userId > 0) {
        resolve({ id: userId, name: "Yahya" }); // نجاح
      } else {
        reject(new Error("Invalid user ID")); // فشل
      }
    }, 1000);
  });
}

// ── استخدام الـ Promise: then / catch ──
fetchUserData(1)
  .then(user => {
    console.log("نجح:", user);
    return user.id; // القيمة المُرجعة تنتقل للـ then الجاي
  })
  .then(id => {
    console.log("الـ ID:", id);
  })
  .catch(error => {
    console.log("حصل خطأ:", error.message);
  })
  .finally(() => {
    console.log("انتهت العملية — نجحت أو فشلت");
  });

// ── حل Callback Hell باستخدام Promise Chaining ──
fetchUser(1)
  .then(user => fetchPosts(user.id))
  .then(posts => fetchComments(posts[0].id))
  .then(comments => console.log(comments))
  .catch(error => console.log(error));
// مسطح وواضح — مفيش تداخل عميق زي Callback Hell

// ── Promise.all: انتظار عدة Promises مع بعض ──
Promise.all([fetchUser(1), fetchUser(2), fetchUser(3)])
  .then(users => console.log(users)); // array بكل النتائج
// أسرع من تنفيذهم واحد واحد — بتشتغل بالتوازي
27
// ASYNC/AWAIT

Async/Await — الشكل النهائي والأوضح

كود async بيقرأ زي كود sync عادي — أنظف طريقة موجودة
⚡ المعيار الحديث 2026

Async/Await مش طريقة جديدة فعلياً — هي "صياغة أجمل" (syntactic sugar) فوق الـ Promises. بتخلي الكود الـ async يبان زي كود عادي متسلسل، وده بيسهّل القراءة جداً.

async-await.js
// ── async function: تعريف دالة تقدر تستخدم await جواها ──
async function getUserData(userId) {
  try {
    // await بيوقف تنفيذ الدالة دي بس (مش الصفحة كلها)
    // لحد ما الـ Promise يخلص، وبيرجع القيمة مباشرة
    const user = await fetchUserData(userId);
    console.log("نجح:", user);
    return user;
  } catch (error) {
    // try/catch بتمسك أي error بدل .catch()
    console.log("خطأ:", error.message);
  }
}

getUserData(1);

// ── مقارنة مباشرة: Promises vs Async/Await ──

// بـ Promises:
function loadDashboard() {
  return fetchUser(1)
    .then(user => fetchPosts(user.id))
    .then(posts => fetchComments(posts[0].id))
    .catch(err => console.log(err));
}

// نفس المنطق بـ Async/Await — أوضح بكثير:
async function loadDashboard() {
  try {
    const user     = await fetchUser(1);
    const posts    = await fetchPosts(user.id);
    const comments = await fetchComments(posts[0].id);
    return comments;
  } catch (err) {
    console.log(err);
  }
}
// بيقرأ زي كود عادي بالترتيب — مفيش .then متتالية

// ── تنفيذ متوازي بـ await + Promise.all ──
async function loadAllUsers() {
  const users = await Promise.all([
    fetchUser(1), fetchUser(2), fetchUser(3)
  ]);
  return users;
}
قاعدة حاسمة: await بيشتغل بس جوّا دالة معرّفة بـ async. لو حاولت تستخدم await في دالة عادية، هتاخد SyntaxError. وتذكّر: try/catch مع async/await بتعمل نفس وظيفة .catch() مع Promises — كلاهما يمسك الأخطاء، بس try/catch أوضح للقراءة.
28
// FETCH API

Fetch API — التواصل الحقيقي مع السيرفرات

إزاي تجيب بيانات حقيقية من الإنترنت — الفورم بتاع الـ Portfolio هيستخدمها
⚡ تطبيق عملي حقيقي
fetch-api.js
// ── fetch: طلب GET بسيط ──
async function getPosts() {
  try {
    const response = await fetch("https://api.example.com/posts");

    // لازم تتأكد إن الطلب نجح فعلياً
    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`);
    }

    // تحويل الرد لـ JSON — برضو عملية async
    const posts = await response.json();
    console.log(posts);
    return posts;

  } catch (error) {
    console.log("فشل التحميل:", error.message);
  }
}

// ── إرسال POST request (مثل Contact Form) ──
async function submitContactForm(formData) {
  try {
    const response = await fetch("https://formspree.io/f/your-id", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(formData) // تحويل Object لـ JSON string
    });

    if (response.ok) {
      console.log("تم إرسال الرسالة بنجاح!");
    }
  } catch (error) {
    console.log("فشل الإرسال:", error);
  }
}

// ── استخدام عملي مع Contact Form ──
const form = document.querySelector("#contactForm");

form.addEventListener("submit", async event => {
  event.preventDefault(); // منع إعادة تحميل الصفحة

  const formData = {
    name: form.name.value,
    email: form.email.value,
    message: form.message.value
  };

  await submitContactForm(formData);
  form.reset(); // تفريغ الفورم بعد الإرسال
});
💡
response.ok ليه مهمة؟

fetch بشكل افتراضي مش بترفض الـ Promise حتى لو السيرفر رجّع error (زي 404 أو 500) — هي بترفض بس لو فيه مشكلة شبكة فعلية. لازم تفحص response.ok يدوياً للتأكد إن الطلب نجح فعلاً، وإلا الكود هيكمل وكأن كل حاجة تمام رغم وجود error.

⚠️
مطبات برمجية شائعة — الوحدة السادسة

1. نسيان await

// مفيش error — لكن النتيجة Promise مش القيمة! async function getUser() { const user = fetchUserData(1); // نسيت await console.log(user); // Promise { <pending> } مش الـ object! }
الحل: أي دالة async لازم تستخدم await قبلها للحصول على القيمة الفعلية. لو نسيت، المتغير هيحمل الـ Promise object نفسه مش النتيجة. ده من أشيع الأخطاء عند المبتدئين في Async/Await.

2. استخدام await خارج async function

Uncaught SyntaxError: await is only valid in async functions
الحل: await ميقدرش يُستخدم إلا جوّا دالة معرّفة بـ async. لو محتاج تستخدم await مباشرة في أعلى الملف (top-level)، تأكد إن بيئة التشغيل بتدعم Top-Level Await (مدعومة في Modules الحديثة).

3. عدم التعامل مع الأخطاء (Unhandled Promise Rejection)

Uncaught (in promise) Error: Failed to fetch
الحل: لو الـ Promise اترفضت (rejected) ومفيش .catch() أو try/catch بيمسكها، المتصفح بيدّيك الـ error ده في الـ Console. دايماً حط try/catch حوالين أي كود فيه await، أو .catch() في نهاية سلسلة .then().
TASK 06
Contact Form حقيقي للـ Portfolio
الوحدة السادسة · Asynchronous JavaScript
  • اربط Contact Form بتاع الـ Portfolio بـ Formspree.io (أو أي خدمة مشابهة) باستخدام fetch
  • استخدم async/await وtry/catch للتعامل مع الإرسال والأخطاء
  • اعرض رسالة "تم الإرسال بنجاح" أو "حصل خطأ" حسب نتيجة الطلب باستخدام DOM manipulation
  • عطّل زرار الإرسال (disabled) أثناء انتظار الرد عشان تمنع إرسال مكرر
  • أضف loading spinner يظهر أثناء الإرسال ويختفي بعدها
  • استخدم Promise.all لتحميل بيانات وهمية متعددة (mock APIs) بالتوازي
Portfolio Step 6 — التواصل الحقيقيالـ Contact Form بقى يبعت رسائل فعلية لإيميلك — مش بس شكل بدون وظيفة.
  • "Formspree setup tutorial" — ربط فورم بإيميلك مجاناً
  • "JSONPlaceholder fake API" — API وهمي للتجربة قبل الـ deployment
  • "JavaScript loading spinner CSS"
UNIT 07
07
// UNIT SEVEN
OOP & Advanced Patterns
البرمجة كائنية التوجه (Object-Oriented Programming) هي إزاي تنظّم كود معقد في "كائنات" منطقية تشبه الأشياء الحقيقية. وهنحل أخيراً لغز this اللي شفناه في مطب الوحدة الرابعة.
4 دروس1 تاسك~80 دقيقة
29
// MENTAL MODEL

OOP Mental Model — التفكير بالكائنات

من "متغيرات ودوال منفصلة" إلى "كائنات منظمة"

تخيل عندك مشاريع كتير في الـ Portfolio. كل مشروع عنده نفس الخصائص (name, description) ونفس السلوكيات (render, filter). بدل ما تكرر نفس الكود لكل مشروع، OOP بتسمحلك تعمل "قالب" (Class) وتنشئ منه نسخ (Instances) بسهولة.

4 مبادئ أساسية في OOP: Encapsulation (تجميع البيانات والدوال المرتبطة في وحدة واحدة)، Abstraction (إخفاء التفاصيل المعقدة وإظهار الواجهة البسيطة بس)، Inheritance (كائن جديد بيرث خصائص من كائن أساسي)، Polymorphism (نفس الدالة بتتصرف مختلف حسب الكائن). هنطبّق المبادئ دي عملياً في الدروس الجاية.

30
// CLASSES

Classes في ES6+ — القالب لإنشاء الكائنات

طريقة منظمة وأنيقة لتعريف "نوع" من الكائنات
⚡ المعيار الحديث للـ OOP
classes.js
// ── تعريف Class ──
class Project {
  // constructor: بيتنفذ تلقائياً عند إنشاء instance جديد
  constructor(name, category, description) {
    this.name = name;
    this.category = category;
    this.description = description;
    this.likes = 0; // قيمة افتراضية
  }

  // methods: دوال متاحة لكل instance
  render() {
    return `<h3>${this.name}</h3><p>${this.description}</p>`;
  }

  addLike() {
    this.likes++;
    return this.likes;
  }
}

// ── إنشاء Instances من الـ Class ──
const portfolio = new Project("Portfolio", "frontend", "صفحتي الشخصية");
const api       = new Project("API", "backend", "سيرفر بيانات");

console.log(portfolio.name);     // "Portfolio"
console.log(portfolio.render()); // "<h3>Portfolio</h3>..."
portfolio.addLike();
console.log(portfolio.likes);    // 1
console.log(api.likes);          // 0 — منفصل تماماً عن portfolio

// ── Getters & Setters: التحكم في الوصول للخصائص ──
class User {
  constructor(name) {
    this._name = name; // _ بتدل على إنها "خاصة" بالاتفاق (مش إلزامي)
  }

  get name() {
    return this._name.toUpperCase();
  }

  set name(newName) {
    if (newName.length < 2) {
      throw new Error("الاسم قصير جداً");
    }
    this._name = newName;
  }
}

const user = new User("yahya");
console.log(user.name); // "YAHYA" — get بيتنفذ تلقائياً
user.name = "Sara";    // set بيتنفذ تلقائياً
31
// INHERITANCE

Inheritance & Private Fields

كائن جديد يرث ويبني فوق كائن موجود، مع حماية حقيقية للبيانات
inheritance.js
// ── Class أساسية ──
class Project {
  constructor(name) {
    this.name = name;
  }

  render() {
    return `<h3>${this.name}</h3>`;
  }
}

// ── Class بترث من Project وتضيف ميزات جديدة ──
class FeaturedProject extends Project {
  constructor(name, badge) {
    super(name); // لازم تنادي constructor الـ parent class
    this.badge = badge;
  }

  // Override: إعادة تعريف method موجودة في الـ parent
  render() {
    const baseHTML = super.render(); // استدعاء النسخة الأصلية من الـ parent
    return baseHTML + `<span class="badge">${this.badge}</span>`;
  }
}

const topProject = new FeaturedProject("Portfolio", "⭐ مميز");
console.log(topProject.render());
// "<h3>Portfolio</h3><span class='badge'>⭐ مميز</span>"

console.log(topProject instanceof Project); // true — ورثت منها

// ── Private Fields: حماية حقيقية (مش بالاتفاق زي _) ──
class BankAccount {
  #balance = 0; // # بتخلي الخاصية خاصة فعلياً — مش متاحة من برّا

  deposit(amount) {
    this.#balance += amount;
  }

  getBalance() {
    return this.#balance;
  }
}

const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance()); // 100
console.log(account.#balance);     // ❌ SyntaxError: Private field
Private Fields (#) هي 2022+ feature حقيقية: قبلها كنا بنستخدم _ كاتفاق بس (نوعية "متعرفش"، مش فرض فعلي). الـ # علامة بتمنع الوصول للخاصية تماماً من برّا الـ Class — حماية حقيقية على مستوى اللغة نفسها.
32
// THIS

this Keyword بعمق — حل لغز الوحدة الرابعة

أهم وأصعب مفهوم في JavaScript — لكن مفهوم بسيط لو شرحناه صح
⚡ يحل مشكلة شائعة جداً

this قيمتها بتتغير حسب إزاي الدالة اتنادت — مش حسب فين اتعرّفت. ده بالظبط اللي خلّى Arrow Functions تتصرف غريب في Event Listeners (شفناها في مطب الوحدة الرابعة).

this-keyword.js
// ── this جوّا method في Object: بتشير للـ object نفسه ──
const user = {
  name: "Yahya",
  greet() {
    console.log(this.name); // "Yahya" — this = user
  }
};
user.greet(); // "Yahya"

// ── this جوّا Regular Function في Event Listener: بتشير للعنصر ──
button.addEventListener("click", function() {
  console.log(this); // <button> نفسه — العنصر اللي اتدوس
  this.classList.toggle("active"); // عملياً مفيدة جداً
});

// ── this جوّا Arrow Function: ماعندهاش this خاص بيها ──
// بتاخده من الـ scope الخارجي (lexical this)
button.addEventListener("click", () => {
  console.log(this); // window أو undefined — مش button!
});

// ── المشكلة الشائعة: this جوّا Class methods كـ callback ──
class Counter {
  constructor() {
    this.count = 0;
  }

  increment() {
    this.count++;
    console.log(this.count);
  }
}

const counter = new Counter();
button.addEventListener("click", counter.increment); // ❌ this بيتلخبط هنا!
// لما addEventListener ينادي increment، this مش هتبقى counter

// ── الحل: Arrow Function wrapper أو .bind() ──
button.addEventListener("click", () => counter.increment()); // ✓ الحل الأسهل
// أو:
button.addEventListener("click", counter.increment.bind(counter)); // ✓ بديل

القاعدة الذهبية لـ this: في Regular Functions، this بتتحدد "وقت النداء" — لو نديت العنصر.method()، this = العنصر. في Arrow Functions، this بتتحدد "وقت التعريف" — بتاخدها من المكان اللي اتكتبت فيه. الفهم ده بيحل 90% من اللخبطة حوالين this.

TASK 07
حوّل بيانات المشاريع لـ Classes
الوحدة السابعة · OOP & Patterns
  • أنشئ Class Project بـ constructor فيه name, category, description, technologies
  • أضف method render() بترجع HTML structure للمشروع كـ Template Literal
  • حوّل بيانات مشاريعك من Array of Objects العادية إلى Array من instances الـ Project
  • اعمل Class تانية FeaturedProject بترث من Project وتضيف badge مميز
  • أضف Private Field #views للعد الخاص بكل مشروع
  • استخدم getter لحساب عدد التقنيات تلقائياً من array technologies
Portfolio Step 7 — كود منظم احترافياًالكود بتاعك دلوقتي بمعايير OOP حقيقية — منظم، قابل للتوسع، وسهل الصيانة على المدى الطويل.
  • "JavaScript Classes vs Functions" — متى تستخدم كل أسلوب
  • "JavaScript this keyword visual explanation"
UNIT 08
08
// UNIT EIGHT · الاحتراف الحقيقي
Performance & الاحتراف
الفرق بين كود "يشتغل" وكود "محترف" هو الأداء والنظافة. هنا هتتعلم المعايير اللي بتفرّق مبرمج junior عن senior.
4 دروس1 تاسك~75 دقيقة
33
// CLEAN CODE

Clean Code Principles

كود يشتغل مش كفاية — لازم يكون مقروء وقابل للصيانة
clean-code.js
// ── ❌ كود سيء: أسماء غامضة ──
function calc(a, b, c) {
  return a * b * (c / 100);
}

// ── ✓ كود نظيف: أسماء واضحة وقصدية ──
function calculateDiscountedPrice(price, quantity, discountPercent) {
  return price * quantity * (discountPercent / 100);
}

// ── ❌ دالة بتعمل أكتر من حاجة واحدة ──
function processUser(user) {
  user.name = user.name.trim();
  validateEmail(user.email);
  saveToDatabase(user);
  sendWelcomeEmail(user);
  // 4 مسؤوليات مختلفة في دالة واحدة — صعب الاختبار والصيانة
}

// ── ✓ Single Responsibility: كل دالة مسؤولة عن حاجة واحدة ──
function cleanUserName(user) {
  return { ...user, name: user.name.trim() };
}
function registerUser(user) {
  const cleanedUser = cleanUserName(user);
  validateEmail(cleanedUser.email);
  saveToDatabase(cleanedUser);
  sendWelcomeEmail(cleanedUser);
}

// ── ❌ Magic Numbers: أرقام بدون معنى واضح ──
if (user.age > 17) { /* ... */ }

// ── ✓ استخدم constants بأسماء واضحة ──
const LEGAL_AGE = 18;
if (user.age >= LEGAL_AGE) { /* ... */ }

5 قواعد ذهبية للـ Clean Code: ١) أسماء متغيرات ودوال واضحة وصفية (مش x وy وtemp). ٢) كل دالة تعمل حاجة واحدة بس (Single Responsibility). ٣) تجنب التكرار — لو كتبت نفس الكود مرتين، اعمله دالة. ٤) Comments بتشرح "ليه" مش "إيه" — الكود نفسه المفروض يشرح "إيه". ٥) constants بدل أرقام عشوائية في الكود.

34
// PERFORMANCE

Layout Thrashing & Performance

إزاي تتجنب إبطاء الصفحة بسبب DOM manipulation غير محسّن
⚡ مهم للمواقع الكبيرة

Layout Thrashing بيحصل لما الكود بيقرأ ويكتب خصائص الـ DOM بالتبادل بشكل متكرر، وده بيجبر المتصفح يعيد حساب الـ Layout مرات كتير غير ضرورية.

performance.js
// ── ❌ Layout Thrashing: قراءة وكتابة متبادلة جوّا loop ──
const boxes = document.querySelectorAll(".box");

boxes.forEach(box => {
  const height = box.offsetHeight; // قراءة (Read) — تجبر المتصفح يحسب layout
  box.style.height = height + 10 + "px"; // كتابة (Write)
  // كل تكرار: قراءة ← كتابة ← قراءة ← كتابة — بطيء جداً مع عدد كبير
});

// ── ✓ الحل: افصل القراءة عن الكتابة ──
const heights = [...boxes].map(box => box.offsetHeight); // كل القراءات مرة واحدة

boxes.forEach((box, i) => {
  box.style.height = heights[i] + 10 + "px"; // كل الكتابات بعدها
});

// ── ❌ تعديل DOM داخل loop مباشرة ──
const list = document.querySelector("#list");
for (const item of items) {
  const li = document.createElement("li");
  li.textContent = item;
  list.append(li); // كل append بيعيد render للصفحة!
}

// ── ✓ استخدم DocumentFragment: تعديل واحد بس للـ DOM ──
const fragment = document.createDocumentFragment();
for (const item of items) {
  const li = document.createElement("li");
  li.textContent = item;
  fragment.append(li); // بيتجمع في الذاكرة، مش بيأثر على الصفحة
}
list.append(fragment); // re-render واحد بس لكل العناصر مع بعض
35
// DEBOUNCE & THROTTLE

Debouncing & Throttling

التحكم في Events متكررة (scroll, resize, input) عشان الأداء
⚡ تقنية احترافية أساسية
debounce-throttle.js
// ── المشكلة: scroll/input بتتفعل مئات المرات في الثانية ──
window.addEventListener("scroll", () => {
  expensiveCalculation(); // بتتنفذ كتير جداً — بطيء جداً!
});

// ── Debounce: تنفّذ الدالة بس بعد ما المستخدم "يهدأ" ──
function debounce(func, delay) {
  let timeoutId;
  return function(...args) {
    clearTimeout(timeoutId); // يلغي أي تنفيذ سابق مجدول
    timeoutId = setTimeout(() => func(...args), delay);
  };
}

const searchInput = document.querySelector("#search");
const debouncedSearch = debounce(query => {
  console.log(`بحث عن: ${query}`); // بتتنفذ بس بعد 500ms من آخر كتابة
}, 500);

searchInput.addEventListener("input", e => {
  debouncedSearch(e.target.value);
});
// مفيدة جداً لـ: search boxes، form validation، resize events

// ── Throttle: تنفّذ الدالة مرة كل فترة محددة بالظبط ──
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

const throttledScroll = throttle(() => {
  console.log("تتنفذ كل 200ms كحد أقصى");
}, 200);

window.addEventListener("scroll", throttledScroll);
// مفيدة لـ: scroll events، animations مرتبطة بالـ scroll
ℹ️
Debounce vs Throttle — إمتى تستخدم إيه؟

Debounce: لما عايز تنتظر "المستخدم يخلص" (search box، form validation). الدالة بتتنفذ مرة واحدة بس بعد فترة هدوء. Throttle: لما عايز تنفيذ منتظم بمعدل ثابت بغض النظر عن التكرار (scroll animations). الدالة بتتنفذ بانتظام كل فترة زمنية محددة.

36
// STORAGE & REGEX

LocalStorage & Regular Expressions

حفظ بيانات في المتصفح، وفحص صيغة النصوص (مثل التحقق من Email)
storage-regex.js
// ── LocalStorage: حفظ بيانات تفضل موجودة حتى بعد إغلاق المتصفح ──

// تخزين قيمة — لازم تكون string
localStorage.setItem("theme", "dark");

// قراءة قيمة
const savedTheme = localStorage.getItem("theme");
console.log(savedTheme); // "dark"

// تخزين Object — لازم تحوّله لـ JSON String الأول
const userPrefs = { theme: "dark", fontSize: 16 };
localStorage.setItem("prefs", JSON.stringify(userPrefs));

// استرجاعه — لازم تحوّله من JSON String لـ Object تاني
const retrieved = JSON.parse(localStorage.getItem("prefs"));
console.log(retrieved.theme); // "dark"

// حذف عنصر أو كل البيانات
localStorage.removeItem("theme");
localStorage.clear(); // حذف كل حاجة — استخدمها بحذر

// ── مثال عملي: Dark Mode بـ localStorage ──
function applyTheme() {
  const theme = localStorage.getItem("theme") ?? "dark";
  document.documentElement.setAttribute("data-theme", theme);
}
applyTheme(); // تطبيق الثيم المحفوظ عند تحميل الصفحة

themeToggle.addEventListener("click", () => {
  const current = document.documentElement.getAttribute("data-theme");
  const newTheme = current === "dark" ? "light" : "dark";
  document.documentElement.setAttribute("data-theme", newTheme);
  localStorage.setItem("theme", newTheme); // حفظ الاختيار
});

// ── Regular Expressions: فحص صيغة نص (مهم لـ Form Validation) ──
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

console.log(emailRegex.test("yahya@dev.com")); // true
console.log(emailRegex.test("invalid-email")); // false

// استخدام عملي في validation
function validateEmail(email) {
  return emailRegex.test(email);
}
💡
RegEx مش لازم تحفظها كاملة

كتابة Regular Expressions معقدة من الصفر مهارة متقدمة. في الواقع العملي، المحترفين بيستخدموا regex patterns جاهزة وموثوقة (زي الـ email pattern اللي فوق) من مصادر موثوقة بدل ما يخترعوها من الصفر. المهم تفهم إزاي تستخدمها بـ .test() مش إزاي تبنيها بالكامل.

TASK 08
Dark Mode وValidation احترافية
الوحدة الثامنة · Performance & الاحتراف
  • اعمل Dark/Light Mode Toggle كامل: زرار، تغيير data-theme، حفظ الاختيار في localStorage
  • أضف Email Validation بـ RegEx على الـ Contact Form قبل الإرسال
  • استخدم debounce() على أي input field عندك (لو فيه search مثلاً)
  • راجع كل دوالك وتأكد من أسماء واضحة (Clean Code) وكل دالة مسؤولة عن حاجة واحدة
  • استخدم throttle على scroll event لو عندك أي scroll-based effect
  • اعمل DocumentFragment لو بتبني أكتر من عنصر مرة واحدة
Portfolio Step 8 — الجودة الاحترافيةالموقع بقى سريع، نظيف الكود، ومحمي من أخطاء الإدخال. مستوى احترافي حقيقي.
  • "JavaScript debounce throttle visual difference"
  • "Chrome DevTools Performance tab tutorial" — لقياس الأداء فعلياً
  • "Email regex pattern JavaScript" — أنماط موثوقة جاهزة
FINAL PROJECT
// FINAL PROJECT
إحياء الـ Portfolio بالكامل
من HTML+CSS ثابتين إلى تطبيق ويب تفاعلي حقيقي
01
هيكلة ملفات JavaScript
قسّم الكود لملفات منطقية: data.js (بيانات المشاريع كـ Classes) ← utils.js (دوال مساعدة: debounce، formatDate) ← main.js (المنطق الرئيسي والـ event listeners). استخدم import/export بينهم.
02
Mobile Menu تفاعلي بالكامل
Hamburger button بيفتح ويقفل القائمة بـ classList.toggle. أضف Event Listener يقفل القائمة تلقائياً عند الضغط على أي link، وآخر يقفلها عند الضغط برّاها (استخدم Event Delegation).
03
Project Filter ديناميكي بالكامل
المشاريع بتترندر من JavaScript (مش HTML ثابت) باستخدام الـ Classes اللي بنيتها. أزرار الفلتر بتستخدم filter() لعرض المشاريع المطابقة فقط، مع animation سلس عند التبديل.
04
Scroll Animations احترافية
استخدم Intersection Observer API (مفهوم جديد — ابحث عنه) لإضافة class "visible" للعناصر لما تدخل الشاشة عند الـ scroll، مع CSS animation من الكورس السابق.
05
Contact Form كامل مع Validation
Email validation بـ RegEx، رسائل خطأ واضحة لكل حقل، إرسال حقيقي بـ fetch وasync/await لخدمة زي Formspree، حالة Loading أثناء الإرسال، رسالة نجاح/فشل بعدها.
06
Dark/Light Mode مع الحفظ الدائم
Toggle كامل، تخزين الاختيار في localStorage، تطبيق الثيم المحفوظ تلقائياً عند فتح الصفحة من جديد (حتى بعد إغلاق المتصفح).
07
صقل الأداء النهائي
راجع كل event listeners وتأكد من استخدام Event Delegation حيث ممكن. أضف debounce لأي input fields. تأكد من نظافة الكود وأسماء واضحة لكل دالة ومتغير.
🎓
الدبلومة اكتملت! الـ HTML بنى الهيكل، CSS أعطاه الجمال، وJavaScript أعطاه الحياة. عندك دلوقتي Portfolio حقيقي احترافي بمعايير عالمية — مش مجرد تمرين تعليمي. الخطوة الجاية: المستوى المستقل الحقيقي.
TASK FINAL 🏆 — مستقل تماماً
اختبار الاستقلالية الحقيقي
التاسك الختامي · بدون أي توجيه خطوة بخطوة

التاسكات اللي فاتت كانت بتوجهك خطوة بخطوة. التاسك ده مختلف — هندّيك المتطلبات بس، وانت اللي هتقرر إزاي تنفذها. ده الاختبار الحقيقي لفهمك، مش حفظك.

  • Portfolio كامل ومتكامل: HTML semantic + CSS احترافي + JavaScript تفاعلي بالكامل، منشور على GitHub Pages
  • كل تفاعل بدون أخطاء في الـ Console — افتح DevTools وتأكد إن مفيش أي error أحمر
  • Performance جيد — استخدم Lighthouse في Chrome DevTools واوصل لـ 85+ في كل المعايير
  • كود نظيف ومنظم في ملفات منفصلة منطقياً بـ Modules
  • Responsive كامل — يشتغل صح على موبايل وتابلت وديسكتوب
  • لو علقت، ارجع للدروس السابقة قبل ما تدور على الحل جاهز — الرجوع للأساسيات بيقوّي فهمك
  • "how to debug JavaScript errors Chrome DevTools" — مهارة الـ debugging أهم من حفظ الحلول
  • شارك المشروع النهائي على LinkedIn أو GitHub — أول خطوة في بناء حضورك كمبرمج
🎉 تهانينا — أنهيت دبلومة Frontend Development كاملة من HTML، لـ CSS، لـ JavaScript — بنيت Portfolio حقيقي من الصفر بمعايير احترافية عالمية. ده مش نهاية الطريق، ده بداية رحلتك الحقيقية كمبرمج. استمر في البناء، استمر في التعلم.