FullStackJavaScript

دليل (this) في الJavaScript

كتب بواسطة: 17/10/2019 لا يوجد تعليقات

كُتب بواسطة Ashay Mandwarya

تعتبر this  كلمة أساسية وهي واحدة من أكثر الكلمات استخداماً والتي كثيراً ما يُساء فهمها في الـJavaScript. سوف أحاول تغيير ذلك في هذا المقال.

لنعود لأيام المدرسة القديمة، حين كنا نتعلم الضمائر.

فلبس يسبح بسرعة. هو يريد الفوز بالسباق.

لاحظ أننا استخدمنا الضمير “هو” ، ولم نكتب اسم فلبس مباشرة، لكننا استخدمنا الضمير هو ليشير إلى فلبس. بالمثل في الـ JavaScript نستخدم this للإشارة إلى كائن في سياق الكود.

مثال:

في الكود السابق لدينا الكائن (object car) والذي لديه الخواص التالية  make و model و fullName. قيمة الـfullName هي دالة تقوم بطباعة الاسم الكلي للسيارة بجملتين مختلفتين.

  • باستخدام this.make+” “ +this.model  ، this هنا تشير إلى الكائن في السياق وهو car لذلك this.make  هي بفاعلية car.make ، وكذلك بالنسبة ل car.model.
  • باستخدام النقطة يمكننا الوصول لخصائص الكائنات (objects، car.make، car.model).

This  هو الحل

الآن فهمنا ما هي this، واستخدامها الأساسي، لنقوم بعمل بعض القواعد على أصابعنا لنتذكرها دائماً.

This  في الـ JavaScript تشير إلى الكائن الذي تنتمي إليه

This  في هذا الكود تشير إلى الكائن car.

تأخذ قيم مختلفة بالاعتماد على الاستخدام

  1. بداخل method.
  2. بداخل الدالة (function).
  3. لوحدها.
  4. في الحدث.
  5. ()Call و ()apply.

بداخل الـmethod

عند استخدام this  بداخل الطريقة، فإنها تشير إلى الكائن المالك لهذه الـ method.

الدوال (functions) المعرفة داخل كائن (object) تسمى method، لنأخذ مثال السيارة مرة أخرى.

fullName هنا هو طريقة (method)، و this  التي بداخل الـ method تنتمي إلى car.

بداخل الدالة

This  بداخل الدالة معقدة قليلاً، أول شيء يجب أن تفهمه هو أن الدوال مثل الكائنات لديها خواص. حين يتم تشغيل أو تنفيذ هذه الدالة، ستقوم باستدعاء الخاصية فى this، والتي هي عبارة عن متغير له قيمة لكائن الذي استدعاه.

هذا فعلياً اختصار ل”كائن سابق”   (antecedent object)- الكائن المُستدعى.

إذا لم يتم استدعاء الدالة بواسطة كائن، فإن this داخل الدالة تنتمي إلى الكائن العام، والذي يسمى نافذة (window). وفي هذا الحالة this  ستشير إلى القيم المعرفة في النطاق العام (global scope). لنرى مثال لتستطيع الفهم بشكل أفضل:

هنا make و model و fullName  معرفين في النطاق العام، لكن الكائن car لديه استدعاء لل fullName ، عندما يتم استدعاؤه بداخل الكائن car فإنه يأخذ تعريف الخواص الموجود بداخل الكائن. من ناحية أخرى فإن الدوال الاثنين الآخرين متشابهان، ويستدعون الخواص المعرفة في النطاق العام.

لوحدها

عندما يتم استدعاء this  لوحدها وليس بداخل كائن أو دالة، فإنها تشير إلى الكائن العام (window).

هنا أشارت this  للخاصية name المعرفة في النطاق العام.

في الحدث (Event)

الأحداث (events) ممكن أن تكون من أي نوع، لكن من أجل أن يكون ذلك بسيطاً وواضحاً، دعونا نأخذ حدث النقر (click).

عندما يتم النقر على زر وحدوث حدث ما، يمكنه استدعاء دالة أخرى للقيام بمهمة معينة عند النقر. إذا تم استخدام this في هذه الدالة فإنها ستشير إلى العنصر الذي قام بهذا الحدث، في DOM جميع العناصر يتم تخزينها ككائنات (objects)، ولهذا السبب عندما يتم تطبيق حدث فإنه يشير إلى العنصر الذي قام بالحدث، الذي هو في الواقع كائن (object) في صفحة الـ DOM  .

مثال:

()call(), apply() & bind

  • Bind (ربط): تسمح لنا بتحديد قيمة this داخل الطريقة (method).
  • Call & Apply (استدعاء وتطبيق): يسمح لنا باستعارة دالة وتعين قيمة this بناءً على استدعاء الدالة.

Call, bind و apply  هم لوحدهم عنوان لموضوع في مقال آخر. هم مهمين جداً وشرحهم هنا غير ممكن، لأنك يجب أن تعرف كل شيء عن this لتعرف استخدامات هؤلاء الدوال.

الجزء الأصعب

لو فهمنا جيداً، فإن this  ستسهل عليك العمل، لكن هناك بعض الحالات غير المفهومة.

المثال 1

حصلنا على نتائج غير متوقعة هنا، قمنا باستعارة دالة تستخدم this من كائن آخر، لكن المشكلة هنا أن الـmethod تم تعيينها فقط لدالة anotherCar ، لكن يتم استدعائها بالفعل في الكائن car، لهذا السبب نحصل على النتيجة Lamborghini  وليس Ferrari.

لحل هذا، نستدعي الدالة ()call.

هنا ()call استدعت ()fullName في الكائن anotherCar والتي لا تحتوي في الأساس على دالة ()fullName.

وسنرى أيضاً أنه حين نكتب car.name و anotherCar.name ستكون النتيجة للسيارة الأخيرة وليس السابقة، مما يعني أنه بالفعل تم استدعاء الدالة في anotherCar وليس في car.

المثال 2

في الكود السابق لدينا كائن عام اسمه cars، ولدينا نفس اسم الكائن بداخل الـ car. الدالة ()fullName عُينت للمتغير vehicle والتي سوف يكون لها اسم بعد ذلك. المتغير ينتمي إلى الكائن العام، لذلك this  تستدعي الكائن العام cars ، بدلاً من الكائن cars وذلك تبعاً للسياق.

لحل هذه المشكلة نستخدم الدالة ()bind.

الربط يساعدنا في تحديد قيمة this، بالتالي المتغير vehicle يشير إلى الكائن car ، وليس للكائن العام، لذلك this  تقع في سياق الكائن car.

المثال 3

في الكود السابق ()fullName  تم استدعائها خلال الدالة والتي تم تكرارها خلال مصفوفة الـ cars باستخدام forEach. بداخل forEach يوجد دالة مجهولة، وذلك خارج السياق، الدالة بداخل دالة في الـ JavaScript يسمى القفل (closure). الـ Closures  مهمين جداً ومستخدمين بكثرة في الـ JavaScript.

مصطلح آخر يلعب دوراً مهماً هنا هو النطاق (scope)، المتغير بداخل الدالة لا يمكنه الوصول للمتغيرات، والخصائص الموجودة خارج نطاقه. This الموجودة بداخل دالة لا يمكنها الوصول لـ this  خارجها. لذلك ليس لديها إلا أن تشير إلى الكائن العام (global object). لكن لا يوجد أي خاصية تم تعريفها هناك، لذلك سيطبع النتيجة undefined.

الحل لما سبق هو أن نعرِّف المتغير خارج الدالة المجهولة، ثم نستخدمه بداخلها.

هنا المتغير self يحتوي على قيمة this الذي يستخدم في الدالة الداخلية، وبالتالي يعطينا الناتج.

المثال 4

هذا مثال تم إعادة النظر فيه، حيث لم يكن this  متاح الوصول إليه، لذلك حافظنا على قيمته باستخدام المتغير self. دعونا نستخدم دالة السهم لحل نفس المشكلة:

مثلما ترى، استخدام دالة السهم في ()forEach قام بحل المشكلة أوتوماتيكياً، ولم نحتاج إلى الربط (bind)، أو إعطاء قيمة this لمتغير آخر. ذلك لأن دوال السهم تربط حسب السياق، لذلك this سوف تشير إلى السياق الأصلي، أو الكائن الأصلي.

forEach قام بحل المشكلة أوتوماتيكياً، ولم نحتاج إلى الربط (bind)، أو إعطاء قيمة this لمتغير آخر. ذلك لأن دوال السهم تربط حسب السياق، لذلك this سوف تشير إلى السياق الأصلي، أو الكائن الأصلي.

المثال 5 

يتكون الكود أعلاه من كائنين متطابقين، أحدهما يحتوي على الدالة callback. دالة الـ callback هو دالة يتم تمريرها إلى دالة أخرى كعامل (argument)، والتي يتم استدعائها في الدالة الخارجية لإكمال نوع من الروتين.

هنا الطريقة ()fullName للكائن truck تتكون من callback، والذي يُستدعى بداخلها أيضاً. الكائن car كما هو في السابق. عندما نقوم باستدعاء طريقة fullName للكائن truck مع الـ (callback argument) ، مثل الـfullName في الكائن car نحصل على نتيجة مشابهة ل Tesla Truck  أو undefined undefined.

بعد قراءتك عن this، البعض منكم سيخمن أن car.fullName يمكن أن يطبع الـ model و يقوم بإنشاء الكائن truck، لكن للأسف this تخدعنا مرة أخرى. هنا يتم تمرير car.fullName كعامل (argument) ولا يتم استدعاؤه بواسطة الكائن truck. الـ callback تقوم باستدعاء طريقة الكائن car، لكن لاحظ أن موقع الاستدعاء الأصلي للدالة هو في callback، و الذي يربط this  بالكائن العام. إنه مُشتت بعض الشيء، لذلك قم بقراءته مرة أخرى.

للتوضيح، قمنا بطباعة this  بنفسها. يمكننا ملاحظة أن this الموجودة في الـ callback تعطي النطاق العام (global scope). لذا للحصول على نتيجة قمنا بإنشاء make  و model كخواص عامة.

بتشغيل الكود مرة أخرى بوجود الخواص العامة make  و model وجدنا نتيجة ل this العامة، وهذا يثبت أن this  تشير إلى الكائن العام (global object).

لنحصل على النتائج المطلوبة، نتيجة car.fullName  سوف تستخدم مرة أخرى ()bind ليربط الكائن car بالـ callback، والذي سيجعل كل شيء صحيحاً مرة أخرى.

تم حلها!

لا شك أن this مفيدة جداً، ولكن لديها صعوبات أيضاً. آمل أن أكون سهلت عليك الفهم، وإذا كنت تريد المزيد من المحتوى المبسط كهذا، اتبعني على Medium ، رجاءً شاركنا بردودك، وانشر هذا إذا أحببته.

اترك تعليق