قواعد الـ this الأربعة — بص على النداء، مش على التعريف
ليه الدرس ده؟ الـ this هي المنطقة اللي بيغرق فيها معظم مطوري JS — وأسئلتها شبه مضمونة في الانترفيوهات. والأهم: هي عكس الـ scope تماماً، والمقارنة دي هي اللي هتثبت الاتنين في دماغك.
🔁 مراجعة سريعة قبل ما تبدأ (~5 دقايق):
- ورقة نموذج التنفيذ — ركز على قسم الـ Scope (بتفتح في تاب جديد)
- اسأل نفسك من الذاكرة: الـ scope chain بتتبني بترتيب إيه — النداء ولا الكتابة؟ — الإجابة دي هي مفتاح الدرس كله. مش متأكد؟ بصة على محاكي 0004
١ — الفكرة في 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)