DEV Community

HowToCodejs
HowToCodejs

Posted on

Fictional Q&A about JavaScript Hoisting

image

Q: Here's a question I'd like answered. Why does this work? What is this Jedi nonsense?


 sayMyNameSayMyName('Morty'); // 'I said your name, Morty.'


function sayMyNameSayMyName(name){
   return `I said your name,${name}.`;
}

Enter fullscreen mode Exit fullscreen mode

A: Hoisting.

Q: Yeah, yeah, but what is hoisting exactly?

A: I will attempt to utilize the gerund that labels this quirky phenomenon to help with the definition. Here goes. ahem Declarations, whether they are a variable or function, are lifted up to the top of your program.

Q: Okay, that's digestible. Sort of. But wait...so you're telling me that this can't be hoisted with the Force?


 sayMyNameSayMyName('Morty'); // TypeError: undefined is not a function

 var sayMyNameSayMyName = function(name){
   return `I said your name,${name}.`;
}

Enter fullscreen mode Exit fullscreen mode

A: Well, you see, declarations get special treatment. Assignments are second class, so to speak. They don't get the privilege of being hoisted.

Q:But why?

A:It's the execution context. That's to blame.

Q:Execution what?

A: Every line of code has a context. There are two key contexts to keep in mind. You have the global and the function context. It looks like this:


/*Global--woohooo I'm freee*/

two(); // 2

function two(){
 /*Function
   ========
*/

 return 2;  


}


Enter fullscreen mode Exit fullscreen mode

Q: Wait. Context is the same as scope, right?

A: Sigh

You have much to learn young Jedi. No, scope refers to access. If a variable is declared in a global scope, it can be accessed by functions or blocks. Functions are unicorns because they create their own scope. But that differs from context.

You see, we can all say that we are from planet Earth. That is our global context. But we cannot all say that we are from Washington DC. That is the function context. In JavaScript, you can determine the current context with the this keyword.

Q: So what does context have to do with hoisting?

A: Yes, so...

First, imagine that the interpreter is an alien who found your signal and is now looking for you. The alien would start on planet earth, our global context. There are seven continents on Earth. It might start in North America.

Q: Why?

A: It loves North America's bizarre geometry.

Anyway, it will then create a scope chain containing your possible country, then your state, then your city, then your street.

Now, let's try to look within the mind of JavaScript's interpreter. When the interpreter reads code, it automatically enters the global context.

The interpreter does something similar to the alien's search tactics by first looking for a function invocation(the signal). It won't execute it until it can create the context(find your info).

There are two stages the interpreter goes through to accomplish its mission: the creation stage and the execution stage.

1) Mind you, we're entering the creation stage.

A function can have multiple functions within it, so the interpreter will initialize a scope chain(country,state,city,street).

It will create a variable object to hold all sorts of arguments, parameters, and function/variable declarations.

It then creates this to store the current context.

This is an oversimplification. We'll simplify it further by only concerning ourselves with how the interpreter deals with function declarations versus variable declarations.

Function:

When the interpreter's nose bumps against a function keyword, it looks for the name. It then stores a reference to that function name in variables object.

Variable:

When the interpreter's nose bumps against a var, let, or any keyword associated with variables, it first stores the variable name in variable objects. Then it automatically initializes it with undefined.

Can you start to see how assigning a function to a variable and hoping it will be hoisted does not work? When we invoke myNameIs(name), the interpreter will find our function expression, but it will only read in the variable name and assign undefined to it.


 sayMyNameSayMyName('Morty'); // 'I said your name, Morty.'
 myNameIs('Morty'); // undefined

//
function sayMyNameSayMyName(name){
   return `I said your name,${name}.`;

}


var myNameIs = function(name){
   return `your name is,${name}.`;
}

Enter fullscreen mode Exit fullscreen mode

You'll understand this more in the next stage.

2) The Execution Stage

In the execution stage, values are assigned to variables within the execution context.

The problem with calling myNameis() early is that the interpreter has assigned undefined tomyNameIs() in the creation stage. If you had invoked myNameIs() after the function expression, the interpreter would have had time to assign the value of myNameIs() within the global context during the execution stage.

Invoking sayMyNameSayMyName() works because a reference to the declared function is stored in the creation stage. When the code is executed, we're able to run it without a problem.

Conclusion

Q: So...hoisting is all about execution context?

A: Yep.

Top comments (5)

Collapse
 
jsn1nj4 profile image
Elliot Derhay

In seriousness, this raises a question in my mind: does choosing to assign instead of declare functions have to do with avoiding weirdness with hoisting or some related issues?

Collapse
 
howtocodejs profile image
HowToCodejs • Edited

To add on to what I wrote, you can also re-assign to a variable that contains an anonymous function. You can take advantage of this by writing an if statement that checks if we are in a certain mode


var strictMode = true;

var logNumber = function(num){
  return num;
}

if(strictMode){
  logNumber = function(num){     
    if(isNaN(num)){
      return; // do nothing
    }else {
      return num; // return number 
    }
  }
}

We would've had to have declared two separate functions if we hadn't created a function expression. Still, the difference is minimal.

Collapse
 
howtocodejs profile image
HowToCodejs • Edited

That's a good question. For one, the concept of hoisting doesn't seem to be something people write their programs around. It's a convenient side-effect that some don't even recognize.

The thing about initialization versus declaration comes down to convention and flexibility.

It is the convention to create a class with a function declaration:

function Bat{...}

But if I wanted to create a function prototype, I would use an assignment operator.

Bat.prototype.fly = function(){...}

Because functions are first order, I can assign them, return them, use them as parameters.

Whether to declare or assign might just come down to style for some, but the differences go beyond hoisting.

Collapse
 
jsn1nj4 profile image
Elliot Derhay

I was thinking in terms of assigning to variables, but thanks for pointing out those other ways of assigning. I don't normally think of it that way with prototype methods and callbacks, but that does make sense.

Collapse
 
jsn1nj4 profile image
Elliot Derhay

Giving it a Unicorn, since...you know...functions are unicorns. :)