قواعد الـ this الأربعة — بص على النداء، مش على التعريف
ليه الدرس ده؟ الـ this هي المنطقة اللي بيغرق فيها معظم مطوري JS — وأسئلتها شبه مضمونة في الانترفيوهات. والأهم: هي عكس الـ scope تماماً، والمقارنة دي هي اللي هتثبت الاتنين في دماغك.
🔁 مراجعة سريعة قبل ما تبدأ (~5 دقايق):
- ورقة نموذج التنفيذ — ركز على قسم الـ Scope (بتفتح في تاب جديد)
- اسأل نفسك من الذاكرة: الـ scope chain بتتبني بترتيب إيه — النداء ولا الكتابة؟ — الإجابة دي هي مفتاح الدرس كله. مش متأكد؟ بصة على محاكي 0004
📖 لو سألتك: "إيه هو الـ 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 بيبقى |
|---|---|---|---|
| 1 | new binding | new Person() | الأوبجكت الجديد اللي بيتبني |
| 2 | explicit binding | f.call(obj) / f.apply(obj) / f.bind(obj) | الـ obj اللي انت حددته بإيدك |
| 3 | implicit binding | obj.f() — فيه نقطة (الرمز . اللي بين الأوبجكت واسم الدالة) | الأوبجكت اللي قبل النقطة |
| 4 | default binding | f() — نداء ساكت من غير أي حاجة | 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);
٤ — الفخ الأشهر في 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: "قطقوطة" });
٨ — المصدر الأساسي للتعمق
- javascript.info — Object methods, "this" — أفضل شرح مكتوب للموضوع.
- MDN — this — المرجع الرسمي الشامل لكل الحالات.
- ورقة المراجعة: reference/js-execution-model.html (اتضاف فيها قسم الـ this)