Home > Mobile >  var hoisting and value between redeclaration
var hoisting and value between redeclaration

Time:09-29

Run below code in Node environment. Running it in browser console doesn't allow to redeclare variable of var.

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

According to hoisting, the above code is going to look like this

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

a variable is being hoisted to the top of the file. JS Engine allocates memory for this variable before the execution. The question is why below code consols 5 instead of undefined.

var a = 5;
console.log(a);
var a = 6;

I'm looking at this code and imagining that it's going to look like this:

var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;

I'd like to be sure of the answer instead of guessing. The JS Engine is smart enough to see that a variable is already declared and is going to ignore the next var expression and rehoisting in such case? So the output should be looking like:

var a = 5;
console.log(a); // 5
a = 6;

So it's like:

  1. JS Engine sees for the first time declaration (in this case along with initialization) of a variable so it's allocating memory.
  2. JS Engine sees for the second time declaration of a variable but is going to ignore the hoisting because variable of given name is already in the memory.

Am I wrong in something?

CodePudding user response:

Preface: In modern JavaScript, var should never be used. Use let or const.


The JavaScript engine handles var in two steps:

  1. Upon entering the global scope or a function scope, it processes every var in the entire scope, defining variables for the them initialized wit the value undefined. If a variable is declared more than once with var, it's exactly as though it were declared once.

  2. Then it starts the step-by-step execution of the code. In that step-by-step execution, any initializer on a var statement (the = 5 in var a = 5) is considered an assignment. So var a = 5 is treated exactly like a = 5 at this point.

So in your example:

var a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;

It's as though you had written this:

var a = 5;
a = undefined;
console.log(a); // undefined
a = 6;

or this:

a = 5;
var a = undefined;
console.log(a); // undefined
a = 6;

or this:

a = 5;
a = undefined;
console.log(a); // undefined
var a = 6;

or this:

var a;
a = 5;
a = undefined;
console.log(a); // undefined
a = 6;

or this:

a = 5;
a = undefined;
console.log(a); // undefined
a = 6;
var a;

or even this (but please don't! :-) ):

var a = 5;
var a = undefined;
console.log(a); // undefined
var a = 6;
var a;

That is, first all the variables declared with var are created (and only once), then the code runs as though any initializers on them were assignments.


This is not how let, const, and class declarations are handled (collectively: lexically-scoped declarations). First: Multiple declarations in the same scope are an error (including if one of them is with var and the other is with one of the lexically-scoped ones). Second: They're hoisted (in Step 1 above), but the hoisted binding¹ is uninitialized until the declaration is executed in the step-by-step code, at which point it's initialized (either with the value from the initializer, or with undefined if it's just e.g. let a;). The time between entry to the scope and the point the binding is initialized is called the Temporal Dead Zone. var doesn't have it because var variables are initialized when they're created (with the value undefined), but let, const, and class declarations do.


¹ The term binding is the general term for variable-like things. In the code:

function example(p) {
    var v;
    let l;
    const c = 42;
    function f() {
    }
    class C {}
}

the bindings created upon entering the example function's scope are p (a parameter), v (a var variable), l (a let variable), c (a const constant), f (the binding created by a function declaration), and C (the binding created by a class declaration). (Note: function and class expressions are handled slightly differently.)

  • Related