The var, let and hoisting nuances in Javascript

Sharath Vignesh
6 min readAug 15, 2018

--

Image by Suresh

What is the difference between the var and let in Javascript ? This is one the favorite questions for the interviewers, and at the drop of the hat, we say “Var is function scoped and let is block scoped”. If you have been doing that, then hey 👋, welcome to the club. I was doing that too, for a really really long period of time. Make no mistake, it is correct, but there is more to it and knowing these nuances is what makes you stand apart from the crowd.

  1. var can be redeclared, whilst let cannot be redeclared.
var a = 5;
var a = 6;
console.log(a); //6, This would work
let b = 1;
let b = 2; // This would throw SyntaxError

2. Var is function scoped and let is block scoped

var + let usage:

In the above snippet, since we have created the variables result and i using var keyword, they can be accessed and used throughout the addArray function anywhere. But, if you see the snippet, it makes sense for result to live throughout the function as it is being returned, but why should the variable i take up the memory space and live as long the function execution is done ? It makes no sense for i to live throughout the function as we need it only within the for loop. Let’s go ahead and modify the snippet.

As let is scoped just to its nearing enclosing parentheses, which in this case is the for loop. As soon as the for loop is done, i is gone and accessing it outside the for loop would throw a ReferenceError.

I have seen in many tutorials where the instructors give you two thumb rules.

  • Use const where ever possible.
  • If it is not possible, then use let. But don’t use var .

I have been doing that too, but now I respectfully disagree with those thumb rules. Instead of completely throwing away var, I learnt from my amazing instructor Kyle on using var, let & const stylistically in the program in such a way that it would improve the readability of the program. Your program might not look fancy with all the es6, but it sure as hell would be easy to understand and read; which in my opinion is far more important than the former. Let me explain that with a simple snippet.

I find this snippet much more intuitive as I can understand that projectDetails which was created using var is going to be used throughout the function, whilst projectID which was created using let is just needed in a line or two and I don’t have to worry about it throughout the function. This is just one example and it might sound lame to some, but I believe these small things are the ones that makes our code stand out, rather than just using let and const everywhere to make it fancier.

(Not everyone with resonate on this, and if you’re not on the same page, then please stick with your style)

3. var is hoisted, while let is also hoisted 😜but … (my fav part)

Yes, you read that right. People often say, var is hoisted to the top, whilst let is not. It is technically not correct. This is quite tricky as it is deep inside the specs.
I’ll first explain it with var.

With hoisting, the code is actually equivalent to

var a;
console.log(a);
a = 2;
console.log(a);
// Javascript moves all the declarations to the top.

When you run any javascript code, it goes thru two passes.

1) Pass 1 is the compile time pass, where all your formal variable and function declarations are added to the scope during this pass

2) Pass 2 is the run time pass, where your instructions are processed.

So lets run the above snippet vocally, just like how the javascript engine does it.

Pass 1 (compile time) :

  • The first formal declaration statement that our compiler encounters is var a=2; (forget the right hand part, consider only LHS)
  • The conversation between complier and global scope goes like this,
    Compiler:Hey scope of global, have you seen the identifier a ? “
    Global scope: No, I’ll create one for you”.
  • When compiler finishes it’s pass, we will have all our formal declarations added to their respective scopes. In our case we just have one formal declaration var a and just one scope (global scope). So, identifier a is added to the global scope.

Pass 2 (runtime) :

  • Before the interpreter executes our first instruction, it does something interesting since we have declared our variables using var keyword.
    It initializes the declarations made using var keyword with the value undefined
  • So after the above step, now our instructions gets executed.
console.log(a); //undefined
/* The interpreter looks for variable a and we have it right there in global scope with the value undefined. */
var a = 2;
// The interpreter assigns new value to a .
console.log(a); //2
/* The interpreter looks for variable a in global scope and, we have it right there with the new value 2.*/

So, variables declared with var keyword, gets hoisted to the top and also gets initialized with undefined before the first execution is processed.

Bear with me a little more.

I’ll now explain the same hoisting with let keyword.

Pass 1 (compile time) :

  • The first formal declaration statement that our compiler encounters is let a=2;
  • The conversation between complier and global scope goes like this,
    Compiler:Hey scope of global, have you seen the identifier a ? “
    Global scope: No, but let me go ahead and create one for you”.
  • When compiler finishes it’s pass, we will have all our formal declarations added to their respective scopes. In our case we just have one formal declaration let a and just one scope (global scope). So, identifier a is added to the global scope.

Pass 2 (runtime) :

  • Here our interpreter does nothing interesting, it just executes our instructions since we don’t have any formal declaration using var.
    Formal declarations made using let do not get initialized.
  • So our interpreter straight away executes our first instruction.
console.log(a); //ReferenceError
  • Our code execution stops with the first line and does not go beyond that.

So, now we can say that both var and let gets hoisted to the top, but the difference is that,

  1. var gets added to the scope and it also gets initialized with undefined.
  2. let on the other hand gets added to the scope, but does not get initialized. It is initialized only at the presence of let keyword, ie, only when statement let a = 2; is encountered. So trying to access a at line 1, gave us ReferenceError.

let and const are hoisted (like var, class and function), but there is a period between entering scope and being declared where they cannot be accessed. This period is the temporal dead zone (TDZ).

Summary:

  1. var can be redeclared, let cannot be redeclared.
  2. var is function scoped, let is block scoped.
  3. var is added to the scope(compile time) and initialised before interpreter proceeds with other statements, let is added to scope (compile time) but does not get initialised at the start. It is initialised only at the presence of let keyword.

Kill it the next time when interviewer asks you about var and let 😄.

Hope you enjoyed ! :)

Thank you for reading. If you find something wrong or better ways to do it, let me know in the comments below.

If you like the post, hit the 👏 button below so that others may find it useful. You can follow me on Twitter.

--

--

Sharath Vignesh
Sharath Vignesh

Written by Sharath Vignesh

Peace-monger . Optimist . Wanderlust . Software Engineer . Foodie

No responses yet