المقال ·  

تحليل عميق لـ JavaScript Hoisting وتأثيره على الكود

Hoisting فى JavaScript هو عملية يقوم بها Interpreter بنقل ال [functions, variables, classes] إلى أعلى Scope الخاص بها قبل عملية تنفيذ الكود في هذه المقال سوف نقوم بشرح هذه العملية بشكل كامل باذن الله تعالي.

Abdelmomen Elshatory

فى هذا المقال ان شاء الله هحاول اشرح ايه هو JavaScript Hoisting وايه المشاكل الى بتحصل بسببه وليه هو موجود اصلا.

Hoisting فى JavaScript هو عملية يقوم بها Interpreter بنقل ال [functions, variables, classes] إلى أعلى Scope الخاص بها قبل عملية تنفيذ الكود. ويقوم Hoisting برفع Declaration فقط ولا يقوم برفع initialization

الفرق بين Declaration و initialization

Abdelmomen Elshatory

طريقة تنفيذ الكود

اللي بيحصل ان قبل عملية تنفيذ الكود المحرك (أو الـEngine) قبل ما ينفذ الكود سطر سطر بياخد لفة كاملة على الكود ويشوف فين [Functions, Variables, Classes] متعرفين علشان يرفع كل واحده فيهم لفوق فى ال Scope بتاعها تقدر تقول ( زى ما بنسخن قبل ماتش الكورة ) والمرحلة دى اسمها مرحلة التجميع (أو ال Compilation Phase) والمرحلة دى بتحصل قبل مرحلة التنفيذ الى هيا (Execution Phase) وبكده نقد نقول ان الى حصل ده هو الى بيسبب عملية Hoisting.

تأثير Hoisting على الكود

اولا على Variables

عندنا 3 احتمالات ممكن يحصلوا فى الكود واحنا بنستخدم اي طريقة من الثلاث طرق (var, let, const) لتعريف متغير والثلاث احتمالات هذه هما

  • Usage => Declaration

  • Usage => Declaration => Initialization

  • Initialization => Usage => Declaration

1. Var

الى بيحصل لما بنعرف متغير بال Var انه المتغير ده بيترفع لأول Scope الخاص به وكما بياخد قيمة ابتدائية وهي undefined وهيا دا المشكلة الاشهر مع استخدام ال var ان فيه ناس كتير متوقعه انها لما تستخدم متغير متعرف ب var قبل سطر تعريفه هو متوقع ان يحصل Error ولكن على العكس مش ده الي بيحصل. الي بيحصل هو ان الكود بيفضل شغال ولكن ممكن النتيجة هيا الى تكون غير متوقعه او مختلفة.

1.1. Usage => Declaration

طباعة المتغير قبل عملية declaration بدون عملية Initialization
النتيجة تكون undefined

Abdelmomen Elshatory
1.2. Usage => Declaration => Initialization

طباعة قبل عملية declaration مع عملية Initialization

النتيجة تكون undefined

Abdelmomen Elshatory
1.3. Initialization => Usage => declaration 

عملية Initialization ثم طباعة ثم declaration

النتيجة المرة دى هتكون قيمة المتغير الى هو فى الحالة دى 9

Abdelmomen Elshatory


2. let & const

طبعا let & const اتعملوا علشان يحلوا المشاكل السابقة الى كانت بتحصل مع var وللعلم هما كمان بيحصلهم Hoisting بس فيه فروقات كبيرة بينهم وبين ال Var لانهم ما بياخدوش أي قيمة ابتدائية زى ما كانت ال var بتاخد undefined.

طب ايه الى بيحصل معاهم.......

الى بيحصل انهم بيدخلوا فى منطقى خاصة اسمها "Temporal Dead Zone" او ما يعرف باسم TDZ ولو حبينا نترجمها بالعربي هيكون اسمها "المنطقة الميتة المؤقتة" بس سيبك من الاسم الى يخض ده

ما هي TDZ ؟

هيا المنطقة او المكان الى فى الكود بيبدء من أول ال scope إلى السطر اللي المتغير بيتعرف فيه بواسطة let او const.

طول ما الكود ما وصلش للسطر الى بيتم تعريف المتغير فيه هيفضل المتغير موجود فى TDZ.

ولو حاولنا اننا نستخدم المتغير ده قبل السطر الى بيتم تعريفه فيه او طول ما هو فى منطقة TDZ هيجيلنا خطأ Error الى هو ReferenceError.

يعني فى الحالة دى المتغير موجود بالفعل ولكن ما نقدرش نستخدمه قبل السطر الى بيتم تعريفه ودا من الأسباب الى بتلغبط بعض الناس ويخليهم يقولوا ان Let & const ما بيحصلهمش معملية Hoisting لا هو بالفعل بيحصل ولكن تأثيره مختلف عن Var.

2.1. Usage => Declaration

طباعة المتغير قبل عملية declaration بدون عملية Initialization
النتيجة تكون ReferenceError

Abdelmomen Elshatory
2.2. Usage => Declaration => Initialization

طباعة قبل عملية declaration مع عملية Initialization

النتيجة تكون ReferenceError

Abdelmomen Elshatory
2.3. Initialization => Usage => declaration 

عملية Initialization ثم طباعة ثم declaration

النتيجة تكون ReferenceError

Abdelmomen Elshatory

ملحوظة : الكلام السابق على let و const فيه معلومة لازم تاخد بالك منها وهو اننا فى حالة استخدام const ما نقدرش نعرف متغير بدون ما نديله قيمة وبالطبع ما نقدرش نعدل في قيمة const يعني فى الحالات السابقة معظمها كنا بنتكلم عن let فقط.

ثانيا على Classess

مش هنحتاج نشرح كتير لان الى بيحصل لل class هو هو الى بيحصل لل let و const.

كل ما ينطبق على let & const ينطبق على ال class.

تعريف ال class بيحصله Hoisting يعني بيترفع ولكن ال class ذات نفسه بيكون فى TDZ لحد ما الكود يوصل بسطر التعريف.

يعني لو حاولنا اننا نعمل Instance من class قبل السطر الى بيتعرف فيه هيحصل نفس المشكلة الى هيا الـReferenceError

ثالثا على Function

1. Function Declarations

function myFunc() { ... }

النوع ده بيترفع بشكل كامل لاول ال scope يعني تقدر تستدعي ال function وتستخدمها قبل سطر تعريفها ومفيش اي مشكلة هتحصل معاك.

ولو تاخد بالك ناس كتير بتعرف النوع ده من function فى اخر الملف تماما من اجل امور تنظيمية وتستخدمها فى اى مكان وما بيحصلش اي مشاكل فى الكود على العكس من اوناع functions تانيه.

2. Function Expressions

let myFunc = function() { ... };
let myFunc = () => { ... };

النوع ده بقا من functions هينطبق عليه نفس قواعد ال variables

يعني لو عرفت الـFunction Expression باستخدام var، يبقى اسم المتغير هو اللي هيترفع، وياخد قيمة undefined.

ولو حاولت تستدعي myFunc() قبل السطر ده، مش هيشتغل وهيجيبك Error غريب ممكن ما تفهمش سببه وهو myFunc is not a function.

myFunc();

var myFunc = function () {
  console.log("This is my function");
};

// Output is : Uncaught TypeError TypeError: myFunc is not a function

ودا بيحصل طبعا لانك بتحاول تستخدم function من متغير قيمته اصلا undefined فطبيعي انه يجيبلك الخطأ ده وخلي بالك انه الخطأ ده من TypeError المرة دي مش ReferenceError.

ولو استخدمنا let او const ساعتها هيجي طبعا خطأ ReferenceError لان هنا اسم المتغير هيترفع ولكن هيكون موجود فى TDZ.

ملخص موضوع ال Hoisting ده:

  1. كل مرة نعرف اي حاجة بيحصلها رفع Hoisting.

  2. var بتترفع وتاخد قيمة أولية undefined وبالطبع ممكن نستخدمها قبل تعريفها ولكن النتيجة هتكون غير متوقعة.

  3. class و let و const بيترفعوا ايضا ولكن فى منطقة TDZ وطبعا ما ينفعش نستخدمهم قبل تعريفهم.

  4. function declarations بتترفع بالكامل، وممكن تستدعيها قبل ما تتعرف.

  5. function expressions بتتعامل زي المتغير اللي شايلها، فلو var هتاخد undefined ويديك TypeError، ولو let او const هتكون في TDZ وتاخد ReferenceError.

نصائح لكتابة كود سليم

  1. استخدم دائما let و const بدل استخدام var.

  2. تأكد ان المتغيرات تم تعريفها قبل الاستخدام فى الكود.

محتويات المقال

© 2025