Hoisting ב- JavaScript

אז למדנו על סוגי התחומים השונים והדרך בה משתנים משתייכים לתחומים בהם הוכרזו. אבל האם ישנה משמעות למיקום הכרזת המשתנים בתחום?

console.log(a); // undefined
var a = 2;

המשמעות של undefined היא שהמשתנה הוכרז אך אין לו ערך (לעומת ReferenceError, האומר שניסינו לגשת למשתנה לא קיים).
איך יכול להיות שהמשתנה קיים אם הכרזנו על המשתנה אחרי שקראנו לו?

הסיבה היא שלפני שהדפדפן מריץ את הקוד ישנו תהליך קומפילציה, בו הקומפיילר קורא את כל הכרזות המשתנים ומפרק אותם לשניים - הכרזות משתנים והשמת ערך אליהם.
כך, הביטוי var a = 2 יפורק ל- var a ו- a = 2. הכרזת המשתנה תועבר לתחילת התחום בו הוכרז, בעוד שהשמת הערך למשתנה תשאר במקום בו נכתבה.

קטע הקוד שלמעלה ייראה כך לאחר שהקומפייר עבר עליו:

var a;
console.log(a);
a = 2;

גם על פונקציות מתבצע Hoisting, אבל לפני שנדבר על כך, בוא ונבדיל בין Function Declaration ל- Function Expression.

function foo() {} // Function Declaration
var foo = function() {} // Function Expression

דרך פשוטה להבדיל בין השניים, היא שכאשר נכריז על פונקציה (Function Declaration), המילה function תבוא תמיד ראשונה בשורת הקוד.
לעומת זאת, כאשר נצהיר על פונקציה בתוך ביטוי כלשהוא (Function Expression), תמיד יופיע קוד מסוים לפני המילה function.

Function Declarations עוברות תמיד לתחילת התחום בו הוכרזו, כך שניתן לקרוא לפונקציה לפני המיקום בו הוכרזה כביכול.

sayHello(); // Hello!

function sayHello() {
  console.log('Hello!');
}

Function Expressions לא עוברות Hoisting. אם שייכנו פונקציה למשתנה, הכרזת המשתנה תועבר לתחילת התחום, אך שיוך הפונקציה יישאר במקום המקורי.

sayHello(); // TypeError: sayHello is not a function

var sayHello = function() {
  console.log('Hello!');
}

כאן המשתנה sayHello הועבר לתחילת הקוד, אך השמת הערך אליו נשארה במקום. מאחר וניסינו להפעיל את המשתנה sayHello כאשר לא שויכה אליו פונקציה, קיבלנו TypeError. כך זה נראה בפועל:

var sayHello;

sayHello(); // TypeError: sayHello is not a function

sayHello = function() {
  console.log('Hello!');
}

הכרזות על פונקציות יופיעו תמיד לפני הכרזרות על משתנים, כפי שניתן לראות בדוגמה הבאה:

sayHello(); // Hello!

function sayHello() {
  console.log('Hello!');
}

var sayHello = function() {
  console.log('Goodbye!');
}

sayHello(); // Goodbye!

קוד זה יפורש כך:

function sayHello() {
  console.log('Hello!');
}

sayHello(); // Hello!

sayHello = function() {
  console.log('Goodbye!');
}

sayHello(); // Goodbye!

לסיכום

  • כל הכרזה, בין אם של פונקציה או של משתנה, עוברת Hoisting ומועברת לתחילת התחום בו הן הוכרזו.
  • השמת ערך למשתנה לא תעבור Hoisting ותשאר במקומה המקורי.
  • הכרזת פונקציות ימוקמו קודם להכרזה על משתנים לאחר ה- Hoisting.