المرحلة 0 · الأسبوع 2 · الدرس 0005 · ~45 دقيقة

قواعد الـ this الأربعة — بص على النداء، مش على التعريف

ليه الدرس ده؟ الـ this هي المنطقة اللي بيغرق فيها معظم مطوري JS — وأسئلتها شبه مضمونة في الانترفيوهات. والأهم: هي عكس الـ scope تماماً، والمقارنة دي هي اللي هتثبت الاتنين في دماغك.

🔁 مراجعة سريعة قبل ما تبدأ (~5 دقايق):

📖 لو سألتك: "إيه هو الـ this؟"

الـ this هو مرجع (reference) بيتحدد تلقائيًا جوه كل function execution context، بيشاور على "الأوبجكت اللي الدالة شغالة لصالحه" في النداء ده تحديدًا. والفرق الجوهري عن المتغيرات العادية: قيمته مش ثابتة بمكان كتابة الدالة — بتتحدد من جديد في كل نداء حسب شكل سطر النداء نفسه، على أساس 4 قواعد لها ترتيب أولوية.

١ — الفكرة في 3 جمل

① الـ this = "الأوبجكت اللي الدالة شغالة لصالحه في النداء ده" — وفاكر من درس 0001 إن كل execution context بيتحدد فيه this وقت إنشائه؟ أهو ده هو.

② المفاجأة الكبرى: الـ this بيتحدد من شكل سطر النداء لحظة التنفيذ — مش من مكان كتابة الدالة. يعني نفس الدالة الواحدة ممكن يبقى ليها this مختلف في كل نداء!

③ فيه بالظبط 4 قواعد بتحدده، وليهم ترتيب أولوية — بتسأل الأسئلة بالترتيب وأول قاعدة تنطبق بتكسب.

⚖️ المقارنة الذهبية (دي اللي بتتحفظ): في درس 0004 حفظنا إن الـ scope بيتحدد بمكان الكتابة (lexical). النهاردة بنضيف نصها التاني: الـ this بيتحدد بطريقة النداء (dynamic). متغيراتك بتتحل بالكتابة، والـ this بيتحل بالنداء — خطين متوازيين ماتخلطهمش.

⚠️ حدود الدرس: الـ arrow functions ليها حكاية مختلفة تماماً مع this — دي درس لوحدها بعد ده إن شاء الله. كل اللي هنا عن الدوال العادية.

٢ — القواعد الأربعة (بترتيب الأولوية)

الأولويةالقاعدةشكل النداءthis بيبقى
1new bindingnew Person()الأوبجكت الجديد اللي بيتبني
2explicit bindingf.call(obj) / f.apply(obj) / f.bind(obj)الـ obj اللي انت حددته بإيدك
3implicit bindingobj.f() — فيه نقطة (الرمز . اللي بين الأوبجكت واسم الدالة)الأوبجكت اللي قبل النقطة
4default bindingf() — نداء ساكت من غير أي حاجةglobalThis (أو undefined في strict mode)

طريقة الاستخدام: بص على سطر النداء واسأل بالترتيب: فيه new؟ فيه call/apply/bind؟ فيه نقطة؟ ولا ولا حاجة؟ — أول "أيوه" بتقفل الموضوع.

📌 يعني إيه "strict mode" اللي في قاعدة 4؟ وضع تشغيل أكثر صرامة للغة (بيتفعل بـ "use strict" أو تلقائياً جوه ES Modules والـ classes) بيحول الأخطاء الصامتة لصريحة — نفس فلسفة الـ TDZ. أثره هنا: النداء الساكت يخلي this = undefined بدل globalThis، فلمسة this.name بترمي TypeError بدل الكتابة على الـ global في صمت. مهم عملياً: كودك في Vue/Nuxt كله ES Modules = strict تلقائياً. القصة الكاملة في الأسبوع 8 إن شاء الله.

✓ القواعد الأربعة وترتيب أولويتهم متحقق منهم بتشغيل Node.js فعلي (2026-08-04).

٣ — شوفها بعينك: الـ this جوه كل context

نفس محاكي الـ Call Stack بتاع 0001 — بس النجم المرة دي هو سطر الـ this جوه كل frame (أخضر = مظبوط زي ما تتوقع، أحمر = الفخ). نفس الدالة hi هتتندى 3 مرات بتلات نتايج مختلفة — توقع قبل كل خطوة!

const user = {
  name: "عمر",
  hi() { console.log("أهلاً " + this.name); }
};
user.hi();
const f = user.hi;
f();
f.call(user);
دوس «خطوة» عشان نبدأ.
Console:
Call Stack + الـ this جوه كل context

٤ — الفخ الأشهر في JS كلها: النقطة الضايعة

const f = user.hi;
f();   // "أهلاً undefined"

اللي حصل في السطرين دول: خدنا الدالة نفسها (والنقطة مش بتتخزن معاها)، وبعدين نديناها نداء ساكت ← فقاعدة default اشتغلت ← فـ this.name طلعت undefined.

لما بتعمل const f = user.hi أنت بتنسخ مؤشر للدالة بس — علاقتها بـ user مش جزء منها. الـ implicit binding بيتحسب من شكل النداء نفسه كل مرة: user.hi() فيها الرمز . فقاعدة 3 بتكسب، أما f() فمن غير نقطة فبتقع للـ default.

🔑 التفرقة اللي بتفك كل اللخبطة: نقطة القراءة ≠ نقطة النداء. user.hi من غير أقواس = قراءة (بتجيب الدالة بس — مالهاش علاقة بالـ this). أما obj2.hi() بالأقواس = نداء، ودي بس اللي بتحدد this. الدالة نفسها مالهاش صاحب ولا ذاكرة — اسأل دايماً: "النقطة اللي جنب أقواس التشغيل ()، قبلها مين؟"

ونفس الفخ ده بيحصل مع الـ callback (يعني: دالة بتديها لكود تاني عشان يناديها هو بعدين — زي addEventListener وsetTimeout):

setTimeout(user.hi, 1000);   // "أهلاً undefined"

اللي بيحصل هنا: أنت بتبعت مؤشر الدالة بس لـ setTimeout (زي const f = user.hi بالظبط)، وبعد ثانية setTimeout بينده النداء من عنده ساكت من غير نقطة ← فبتقع لقاعدة default ← فبتطبع "أهلاً undefined".

هتقابل الفخ ده بشكل يومي في شغلك الحقيقي مع الـ events والـ timers. ✓ متحقق منه بتشغيل Node فعلي (2026-08-05) — والحلول (bind والـ arrow functions) هما بالظبط الدرسين الجايين إن شاء الله.

٥ — توقع قبل ما تشوف (3 تمارين)

const user = { name: "عمر", hi() { console.log("أهلاً " + this.name); } };
const greet = user.hi;
greet();
user.hi.call({ name: "سارة" });
const obj2 = { name: "نور", hi: user.hi };
obj2.hi();

٦ — اختبر نفسك

س1: user.hi.call(other) — فيه نقطة وفيه call مع بعض. مين this؟

س2: إيه اللي بيحدد قيمة this لدالة عادية؟

س3: ليه const f = user.hi; f(); بتفقد الـ this؟

س4 (سؤال judgment): الـ scope بالكتابة والـ this بالنداء — إيه النتيجة العملية للفرق ده؟

٧ — التحدي الأخير: اشرحها بصوت عالي

من غير ما تبص على أي حاجة — امشي على السطور دي سطر سطر، وقول عند كل نداء: أنهي قاعدة انطبقت، وليه، وإيه اللي اتطبع — واقفل بحالة البرنامج النهائية:

const cat = { name: "بسبس", speak() { console.log(this.name + " بيقول ميو"); } };
cat.speak();
const talk = cat.speak;
talk();
talk.call({ name: "قطقوطة" });

٨ — المصدر الأساسي للتعمق