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

طريقة تنفيذ الكود
اللي بيحصل ان قبل عملية تنفيذ الكود المحرك (أو الـ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

1.2. Usage => Declaration => Initialization
طباعة قبل عملية declaration مع عملية Initialization
النتيجة تكون undefined

1.3. Initialization => Usage => declaration
عملية Initialization ثم طباعة ثم declaration
النتيجة المرة دى هتكون قيمة المتغير الى هو فى الحالة دى 9

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

2.2. Usage => Declaration => Initialization
طباعة قبل عملية declaration مع عملية Initialization
النتيجة تكون ReferenceError

2.3. Initialization => Usage => declaration
عملية Initialization ثم طباعة ثم declaration
النتيجة تكون ReferenceError

ملحوظة : الكلام السابق على 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 ده:
كل مرة نعرف اي حاجة بيحصلها رفع
Hoisting
.var بتترفع وتاخد قيمة أولية undefined وبالطبع ممكن نستخدمها قبل تعريفها ولكن النتيجة هتكون غير متوقعة.
class و let و const بيترفعوا ايضا ولكن فى منطقة
TDZ
وطبعا ما ينفعش نستخدمهم قبل تعريفهم.function declarations بتترفع بالكامل، وممكن تستدعيها قبل ما تتعرف.
function expressions بتتعامل زي المتغير اللي شايلها، فلو var هتاخد undefined ويديك TypeError، ولو let او const هتكون في
TDZ
وتاخد ReferenceError.
نصائح لكتابة كود سليم
استخدم دائما let و const بدل استخدام var.
تأكد ان المتغيرات تم تعريفها قبل الاستخدام فى الكود.