ما هذا هنا؟ التشغيل الداخلي لكائنات JavaScript


الصورة: غريبة ليليانا صائب (CC BY 2.0)


جافا سكريبت هي لغة متعددة النماذج تدعم البرمجة الموجهة للكائنات والربط الديناميكي. يعد الارتباط الديناميكي مفهومًا قويًا يسمح لك بتغيير هيكل كود JavaScript في وقت التشغيل ، ولكن يتم تحقيق هذه القوة الإضافية والمرونة على حساب بعض الالتباس ، ومعظمها يرتبط this السلوك في JavaScript.


ربط ديناميكي


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


لنلعب لعبة. أنا أسميها "ما this هنا؟"


 const a = { a: 'a' }; const obj = { getThis: () => this, getThis2 () { return this; } }; obj.getThis3 = obj.getThis.bind(obj); obj.getThis4 = obj.getThis2.bind(obj); const answers = [ obj.getThis(), obj.getThis.call(a), obj.getThis2(), obj.getThis2.call(a), obj.getThis3(), obj.getThis3.call(a), obj.getThis4(), obj.getThis4.call(a) ]; 

فكر فيما ستكون عليه القيم في صفيف answers وتحقق من إجاباتك باستخدام console.log() . خمنت؟


لنبدأ بالحالة الأولى ونستمر بالترتيب. إرجاع obj.getThis() undefined ، ولكن لماذا؟ وظائف السهم لا تملك أبدا this . بدلاً من ذلك ، يأخذون دائمًا this من النطاق المعجمى ( تقريبا. معجمى هذا ). بالنسبة لجذر الوحدة النمطية ES6 ، سيكون للمنطقة المعجمية قيمة undefined من this . obj.getThis.call(a) أيضًا غير معرف لنفس السبب. لوظائف السهم ، لا يمكن تجاوز هذا ، حتى مع .bind() أو .bind() . this سوف يؤخذ دائما من المجال المعجمى.


يحصل obj.getThis2() على الربط أثناء استدعاء الأسلوب. إذا لم يكن هناك هذا الربط لهذه الوظيفة من قبل ، فيمكن أن يكون هذا مرتبطًا this (نظرًا لأن هذه ليست وظيفة سهم). في هذه الحالة ، this كائن obj مرتبط في الوقت الذي يتم استدعاء الأسلوب به . أو [squareBracket] بناء جملة الوصول إلى الخاصية. ( لاحظ الارتباط الضمني )


obj.getThis2.call(a) أكثر تعقيدًا قليلاً. call() الأسلوب call() استدعاء دالة مع هذه القيمة المعطاة والوسائط الاختيارية. بمعنى آخر ، تحصل الطريقة على this الربط من المعلمة obj.getThis2.call(a) ، لذا تقوم obj.getThis2.call(a) بإرجاع الكائن. ( لاحظ ملزم صريح )


في حالة obj.getThis3 = obj.getThis.bind(obj); نحن نحاول الحصول على دالة سهم باستخدام this الربط ، والتي ، كما سبق لنا أن obj.getThis3() ، لن تنجح ، لذلك نحصل على undefined لـ obj.getThis3() و obj.getThis3.call(a) على التوالي.


بالنسبة للطرق العادية ، يمكنك الربط ، لذا obj.getThis4() بإرجاع obj ، كما هو متوقع. لقد حصل بالفعل على obj.getThis4 = obj.getThis2.bind(obj); هنا obj.getThis4 = obj.getThis2.bind(obj); و obj.getThis4.call(a) يأخذ في الاعتبار الربط الأول وإرجاع obj بدلاً من.


الكرة الملتوية


سنحل نفس المشكلة ، لكننا نستخدم هذه المرة class يحتوي على حقول عامة لوصف الكائن (تتوفر ابتكارات المرحلة 3 في وقت كتابة هذا التقرير في Chrome افتراضيًا ومع @babel/plugin-offer-class-properties ):


 class Obj { getThis = () => this getThis2 () { return this; } } const obj2 = new Obj(); obj2.getThis3 = obj2.getThis.bind(obj2); obj2.getThis4 = obj2.getThis2.bind(obj2); const answers2 = [ obj2.getThis(), obj2.getThis.call(a), obj2.getThis2(), obj2.getThis2.call(a), obj2.getThis3(), obj2.getThis3.call(a), obj2.getThis4(), obj2.getThis4.call(a) ]; 

فكر في الإجابات قبل المتابعة.


هل انت جاهز


كافة المكالمات باستثناء obj2.getThis2.call(a) بإرجاع مثيل الكائن. obj2.getThis2.call(a) لا تزال وظائف السهم تحصل على this من بيئتها المعجمية. هناك اختلاف في كيفية تعريف this من البيئة المعجمية لخصائص الفصل. في الداخل ، تبدو تهيئة خصائص الفصل كالتالي:


 class Obj { constructor() { this.getThis = () => this; } ... 

بمعنى آخر ، يتم تعريف وظيفة السهم ضمن سياق المنشئ. نظرًا لأن هذه فئة ، فإن الطريقة الوحيدة لإنشاء مثيل هي استخدام الكلمة الأساسية new (سيؤدي حذف new إلى حدوث خطأ). أحد أهم الأشياء التي تقوم بها الكلمة الرئيسية new هو إنشاء مثيل جديد للكائن وربطه في المُنشئ. هذا السلوك ، بالإضافة إلى السلوكيات الأخرى التي ذكرناها أعلاه ، يجب أن يشرح الباقي.


استنتاج


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


إذا كان الأمر أصعب مما توقعت ، فأنت لست وحدك. لقد طلبت الكثير من المطورين حول هذا الموضوع وأعتقد أن واحدًا منهم فقط قد تعامل مع هذه المهمة.


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


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


ومع ذلك ، في بعض الأحيان this مفيد. على سبيل المثال ، لتبادل الأساليب بين عدد كبير من الكائنات. حتى في البرمجة الوظيفية ، يمكن استخدام هذا للوصول إلى أساليب كائن أخرى من أجل تنفيذ التحولات الجبرية اللازمة لبناء جبر جديد أعلى من تلك الموجودة. لذلك ، يمكن الحصول على this.map() باستخدام this.map() و this.constructor.of() .




شكرًا للمساعدة في ترجمة wksmirnowa و VIBaH_dev

Source: https://habr.com/ru/post/ar452192/


All Articles