English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

فهم عميق لـ scope وhoisting في JS

نطاق العمل (Scoping)

للمبتدئين في لغة البرمجة Javascript، من أكثر الأماكن التي تشوبها الحيرة هو نطاق العمل (Scope);في الواقع، ليس فقط المبتدئين. لقد رأيت بعض المبرمجين المتمرسين في Javascript، لكنهم لم يفهموا نطاق العمل بشكل عميق. الحيرة في نطاق العمل في Javascript يرجع إلى أن لغة البرمجة نفسها تبدو طويلة وكأنها من عائلة لغة البرمجة C. فهمي لنطاق العمل هو أنه سيكون له تأثير فقط على نطاق معين وليس له تأثير على الخارج، وهو مساحة مغلقة. في هذه المساحات، لا يمكن الوصول إلى المتغيرات الداخلية من الخارج، لكن يمكن الوصول إلى المتغيرات الخارجية من الداخل.

تعريف المتغيرات في لغة البرمجة c يأتي على شكل متغيرات عالمية ومحلية، ويكون نطاق المتغيرات العالمية هو أي ملف أو وظيفة يمكن الوصول إليها (بالطبع، بالنسبة لمستندات c الأخرى التي لا تحتوي على تعريف المتغير، يجب استخدام كلمة المفتاح extern للإشارة إليها، ويمكن أيضًا استخدام كلمة المفتاح static لتقييد نطاق العمل في الملف الحالي)، ويكون نطاق المتغيرات المحلية هو من النقطة التي تم الإشارة إليها حتى آخر حاشية تتضمن منطقة البلاك سكوب.

ما هي نطاقات JS؟

في ES5، لدى JS فقط نوعان من النطاقات: النطاق العالمي والنطاق الداخلي للدالة.

نطاق النطاق العالمي هو نطاق النطاق العالمي، يمكن الوصول إليه في أي مكان (إذا لم يتم تغطيته بالنطاق الداخلي للدالة).

نطاق دالة الدالة ليس نفسه لنطاق المتغيرات المحلية في C، نطاقه هو نطاق الدالة بأكمله، بغض النظر عن مكان إعلانه! هذا ما يُسمى رفع المتغيرات، وهو مفهوم رفع المتغيرات. لكن لا تقلق، سيتم تفسير رفع المتغيرات بشكل خاص لاحقًا.

لكن في ES6، تم إضافة نطاق مكتوب في الكتل (يغطي الكتلة الأحدث)، ولكن فقط للمتغيرات المعلنة باستخدام let.
عرض النطاق:

عندما يتم تعريف المتغير دون كتابة var، مثل i=0، يتم تعريفه كمتغير عالمي، منطقته هي النطاق العالمي، وإلا فإنه يكون متغيرًا محليًا، منطقته هي نطاق الدالة. السبب في أن var i=0 يُعتبر متغيرًا عالميًا هو أنه تم إعلانه في منطقة العالمية، وليس في نطاق الدالة، لذا فهو نفس i=0.

إذاً، لماذا تكون النتيجة هكذا؟ استمر في القراءة لمعرفة ذلك.

شكل الإعلان

إعلان المتغير:

إعلان الدالة:

رفع المتغيرات (Hoisting)

يطرح سؤالًا

ما الذي سيتم إخراجه من هذا الكود؟

لقد سألت العديد من الناس عن هذا السؤال، قال معظمهم أن النتيجة هي تاريخ. لكن النتيجة الحقيقية هي undefined. لماذا هذا؟ هنا يأتي مفهوم - رفع المتغيرات، ما يعنيه باللغة الصينية هو رفع المتغيرات. وفقًا لموسوعة MDN، فهذا هو تفسير رفع المتغيرات:

رفع المتغيرات

لأن إعلانات المتغيرات (وإعلانات عامة) يتم معالجتها قبل تنفيذ أي كود، فإن إعلان متغير في أي مكان في الكود يمثل إعلانه في الأعلى. هذا يعني أيضًا أن المتغير يمكن أن يبدو أنه يستخدم قبل إعلانه. هذا السلوك يُسمى "الرفع"، لأنه يبدو أن إعلان المتغير قد تم رفعه إلى أعلى الكود أو الكود العالمي.

هذا المقطع يمكن ترجمته إلى

بما أن دالة الديclaration يتم معالجتها قبل تنفيذ أي كود، فإن دالة الديclaration في أي مكان في منطقة الكود هي نفسها التي يتم دفعها في أقصى بداية (أعلى). أي أن يبدو أن المتغير يمكن استخدامه قبل الديclaration! هذا السلوك يُدعى "hoisting"، وهو رفع المتغيرات، يبدو كما لو أن دالة الديclaration قد تم نقلها تلقائيًا إلى أعلى الكود الخاص بالوظيفة أو الكود العالمي.

ملاحظة:تم رفع الديclaration فقط، وليس التوليد.

إذن، هذا الكود في الواقع هو شكل الكود التالي:

لذلك، يجب فهم أن tmp هو مجرد الديclaration دون التوليد عند إخراج console، لذا يجب أن يكون الإخراج هو undefined.

هذا ما يجب توضيحه، على الرغم من أن جميع الديclarations (بما في ذلك var في ES5،function،وfunction *،let،const،class في ES6) سيتم رفعها، إلا أن رفع var،function،function *،وlett،const،class ليست نفسها! يمكن رؤية السبب فيهذا التوضيح(بمعنى أن let،const،class تم رفعها أيضًا، ولكنها لن تتم التوليد، عند الوصول إليها سيتم عرض استثناء ReferenceError، ويجب أن يتم التوليد في وقت تنفيذ الكود، وسيتم إعطاء حالة ما قبل التوليد اسمها temporal dead zone). لننظر إلى كود لفهم ذلك:


تم رفع a، ولكن لأن التعريف كان في النهاية، فإن الإخراج هو undefined

على الرغم من أن a تم رفعه، إلا أن هناك خطأ في الاستدلال!

لماذا أو لماذا هذا

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

تعريف مكرر

الإخراج في الواقع هو: 1 2 2. على الرغم من أن x تمت تعريفه مرتين، ولكن كما ذكرنا، متغيرات var في js لها مجال النطاق العالمي فقط والمجال النطاق الداخلي للوظيفة، ويتم رفع الديclaration، لذا فإن x سيتم تعريفه مرة واحدة فقط في أقصى بداية، سيتم تجاهل تعريف var x=2، وسيتم استخدامه فقط للإسناد. أي أن الكود المذكور في الواقع هو نفسه الكود التالي.

مشكلة رفع الوظيفة والمتغير في نفس الوقت

ما الذي سيحدث إذا تمت تعريف نوع الوظيفة والمتغير في نفس الوقت؟ انظر إلى الكود أدناه


A

النتيجة الصادرة في الواقع هي: function foo(){}، أي محتوى الدالة

وإذا كان الأمر كالتالي


B

لكن النتيجة أصبحت: undefined

لماذا يكون الأمر كذلك؟

كان هناك نوعان من رفع الدوال

      نوع واحد: بيانات الدالة. وهو A، function foo(){} هذا الشكل

     نوع آخر: تعبير الدالة. وهو B، var foo=function(){} هذا الشكل

النوع الثاني في الواقع هو تعريف متغير، لذا فإن النتيجة الصادرة من B يمكن فهمها كـ undefined

بينما يتم رفع النوع الأول من بيانات الدالة مع تعريفها كاملة، لذا فإن A هو مكافئ للطريقة التالية!

السبب في ذلك هو: 1- يتم رفع بيانات الدالة إلى أعلى، 2- يتم تعريف المتغير مرة واحدة فقط، لذا سيتم تجاهل تعريف var foo='i am text' الذي يأتي بعد ذلك

وتبعاً لذلك، يتمتع بيانات الدالة بمرتبة أولوية أعلى من بيانات المتغيرات، لذا فإن النوع التالي من الصيغ هي نفس محتوى الدالة:

الخاتمة

للتفكير بشكل كامل في مجال النطاق وHoisting في JS، يكفي تذكر النقاط الثلاث التالية:

      1- يتم رفع جميع التعريفات إلى أعلى مجال النطاق

      2- يتم تعريف نفس المتغير مرة واحدة فقط، وبالتالي سيتم تجاهل جميع التعريفات الأخرى

      3- يتمتع بيانات الدالة بمرتبة أولوية أعلى من بيانات المتغيرات، وسيتم رفع بيانات الدالة مع تعريفها

ملاحظة:

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

هذا هو نهاية محتوى هذا المقال، آمل أن تكون محتويات هذا المقال قد ساعدتكم في التعلم أو العمل، إذا كان لديكم أي استفسارات، يمكنكم ترك تعليقات للتفاعل.

أنت قد تحب