مستوى المقال: متوسط. الكلام ده موجّه لحد كتب JavaScript قبل كده، وعارف يعمل function و object، لكن كلمة this لسه بتفاجئه بنتايج غريبة. لو لسه بتبدأ من الصفر، اقرأه على مهلك؛ فيه مثال من الحياة قبل كل جزء تقني.
ليه this بيتغيّر معناه في JavaScript
لو زرار شغّال تمام عندك دلوقتي بيرمي Cannot read properties of undefined بمجرد ما تنقله جوّه setTimeout أو تبعته كـ callback، المشكلة مش في الكود اللي جوّه الدالة. كلمة this بصّت لمكان تاني. المقال ده بيديك أربع قواعد تحسم قيمة this في أي سطر من غير تخمين، وبيوضّح ليه الدالة السهمية بتحل أغلب الوجع ده.
المشكلة باختصار
أغلب المطورين بيفتكروا إن this بتشير لـ«الكائن اللي الدالة متعرّفة جوّاه». الفكرة دي غلط، وبتفشل بالظبط في اللحظة اللي بتنقل فيها الدالة من مكانها: تبعتها لـ addEventListener، تخزّنها في متغير، تمرّرها لـ map. this في JavaScript مش مربوطة بمكان كتابة الدالة. هي مربوطة بطريقة استدعائها. الفرق ده بالظبط هو سبب أغلب الـ bugs اللي ليها علاقة بـ this.
مثال من الحياة قبل الكود
قبل أي كود، خد المثال ده. كلمة «أنا» في اللغة العربية مالهاش صاحب ثابت. لمّا تقول انت «أنا جعان»، الكلمة بتشير ليك. لمّا صاحبك يقول نفس الجملة، نفس الكلمة بتشير له هو. الكلمة اتنطقت بنفس الحروف، لكن اللي بتشير له بيتحدّد ساعة النطق، مش وقت ما الكلمة اتكتبت في القاموس.
this هي بالظبط كلمة «أنا» بتاعة الدوال. لمّا تكتب this جوّه دالة، انت مش بتحدّد قيمتها. القيمة بتتحدّد كل مرة الدالة بتتنادى فيها، وبتعتمد على إزاي نادتها. نفس الدالة ممكن ترجّع this مختلفة في كل استدعاء. ده مش عيب في اللغة؛ ده تصميم مقصود اسمه الربط الديناميكي (dynamic binding).
التعريف الدقيق
بعد المثال، التعريف العلمي. كل استدعاء لأي دالة في JavaScript بيعمل سياق تنفيذ (execution context). من ضمن مكوّنات السياق ده حاجة اسمها this binding. القيمة دي بتتحطّ لحظة الاستدعاء حسب صيغة النداء، وبتفضل ثابتة طول ما الدالة شغّالة. يعني this مش متغيّر عادي ومش بتورّث من الـ scope زي باقي المتغيرات؛ هي قيمة بتُحقَن من جديد في كل استدعاء.
القواعد الأربعة بالترتيب
عشان تعرف قيمة this في أي سطر، بُصّ على صيغة الاستدعاء وطبّق القواعد دي. أول قاعدة تتحقّق هي اللي بتحسم القيمة.
- الاستدعاء الحر:
fn()من غير أي حاجة قبلها. هناthisبتساويundefinedفي الوضع الصارم strict mode، أو الكائن العام (windowفي المتصفح) من غيره. - الاستدعاء كميثود:
obj.fn(). بتساوي الكائن اللي على يسار النقطة، يعني .