36

I do not fully understand why the following displays "hoisted" towards the end.

var x = 'set';
var y = function () 
{
    // WHAT YOU DON'T SEE -> var x; 
    // is effectively "hoisted" to this line!

    if (!x) 
    { 
        // You might expect the variable to be populated at this point...it is not
        // though, so this block executes
        var x = 'hoisted'; 
    }

    alert(x); 
}

//... and this call causes an alert to display "hoisted"
y();

Any pointers would be appreciated.

4
  • 1
    it's equiv to var y = function () { var x; ..., which leaves x local to the function and un-assigned. if blocks won't "stop" var from hoisting Commented Apr 16, 2015 at 6:47
  • Yes, var x hoists to the top of the scope, this is normal behaviour of variable declaration in JS. Commented Apr 16, 2015 at 6:49
  • 2
    @Qantas94Heavy: This one might actually become a good canonical duplicate for variable hoisting, afaik we don't have one currently. Only for function hoisting Commented Apr 16, 2015 at 17:19
  • @Bergi: yeah, this seems broader than the question I linked. I'll mark the other question as a duplicate of this one. Commented Apr 17, 2015 at 1:11

7 Answers 7

36

Quoting MDN Docs on var hoisting,

Because variable declarations (and declarations in general) are processed before any code is executed, declaring a variable anywhere in the code is equivalent to declaring it at the top. This also means that a variable can appear to be used before it's declared. This behavior is called "hoisting", as it appears that the variable declaration is moved to the top of the function or global code.

So, in your case, JavaScript knows that a local variable (not the one declared outside) x is defined somewhere in the function, but it does not know the actual value of it until the execution reaches an assignment statement which assigns to x. (Declarations are processed during the compile time and assigments are done in the execution time) Till the assignment is done, the default value undefined will be used. Since undefined is falsy, the condition

if (!x) {

is satisfied and the assignment statement is executed. That is why you are getting hoisted in the alert box.


Let's say you have not declared x inside the function,

var x;

var y = function () {
    if (!x) {
        x = 'hoisted';
    }
    alert(x);
}

y();
alert(x);

Here, since x is not declared anywhere within the function, at runtime, JavaScript will look for x in the higher scopes. In this case, it finds it right outside the function. So, that x will be used. Since you assigned hoisted to x, the inner alert will also say hoisted and after leaving the function, alert(x) will also alert hoisted.

Sign up to request clarification or add additional context in comments.

Comments

12

Variable declarations hoist to the top of the scope. So your code is equivalent to this:

var x = 'set';
var y = function () {
    var x;
    if (!x) {
        x = 'hoisted';
    }
    alert(x);
}

y();

When y is executed, var x shadows outer scope x so inside of the y function x is undefined after the first line of declaration.

4 Comments

how can get value of global variable in function ?
@mhkyazd using the global object.
@mhkyazd simply do not write var x anywhere in the function if x comes from an outer scope so it is not overriden.
@mhkyazd If you want to use global var inside your function, then don't redeclare x again inside.
2

this answer explain in very detail the different scopes of the variables. In your particular case you can use the reference to:

this.x; 

To access to the global x variable outside the function. Because inside of the function you are trying to access to an undefined variable, using the this keyword you make a reference to variables outside of the function.

And the part of

if(!x)

It's true because you are testing: is false and x in that point is undefined because does not exist in the function scope, and undefined is consider one of the false values in JS, for more info please take a look here.

1 Comment

Note that using this works only in this specific case as x is being called directly -- if it's put onto a different object then this will no longer refer to the global object.
0

In javascript console.log inside a function gives undefined as output for unpassed parameters but outside the function it gives not defined error as inside a function browsers explicitly declares the variable. For e.g

console.log(x) 

gives VM1533:1 Uncaught ReferenceError: x is not defined whereas

function test(x) {
console.log(x)
}
test(); 

gives undefined. It is because the function test() is rewritten by browser as:

function test(x) {
    var x;
    console.log(x)
    }

Another Example : -

var x =5 ;
function test(x) {
console.log(x)
}
test(); 

is still undefined as the function becomes

function test(x) {
    var x;
    console.log(x)
    }

The alert in the below example will give undefined :-

    var x =5;
    function test() {
    alert(x);
    var x =10;
    }

test(); 

The above function will become :-

 function test() {
    var x;
    alert(x);
    x =10;
    }

The scope of javascript variable inside a function is function level scope and not block level. For e.g.

function varScope() {

for(var i = 0; i < 10; i++){
    for(var j = 0; j < 5; j++){}
        console.log("j is "+j)
    }
    console.log("i is "+i);


}
varScope();

will give output as :

j is 5 
i is 10

Again the function has become as :-

  function varScope() {
    var i;
    var j;
    for(i = 0; i < 10; i++){
        for(j = 0; j < 5; j++){}
            console.log("j is "+j)
        }
        console.log("i is "+i);
    }

Comments

0

Using this keyword helps to reference a variable outside a function which is hoisted. In your case

   this.x

Update: As of ES2015, using const and let keywords makes variables not to be hoisted.

Example: Hoisted global Variable in a function

 var x = "global variable hoisted";
function check_hoisting(){
 alert(x); //undefined because of js hoisting
var x = "local variable";
 }
 check_hoisting();

Example: Variable not hoisted due to let keyword

let x = "global variable not hoisted";
function check_hoisting(){
 alert(x); 
var x = "local variable";
 
  }
    check_hoisting();

Comments

0

JavaScript only hoist declarations, not initializations.

Comments

-1

As a new programmer, I could not figure out the solution to this issue, no matter how many answers I read in this or similar questions. So I'm sharing my solution for others like me arriving here from desperate searching (plus all the related questions looking for a fix are locked and link to this question). So here's an answer to your question that newbies like me can understand how and why to change our code to avoid this issue and have the variables return the values we expect. My issue is that when learning javascript, I assumed every time I wanted to modify a variable I had to start the line with var like:

var myVariable = 1;
...
var myVariable = myVariable + 3;

when in reality, you should only put var the first time (when you declare/initialize a variable). All other times after that, you have to start the line with the variable name and drop the var like:

var myVariable = 1;
...
myVariable = myVariable + 3;

I didn't see an issue with how I was doing it at first, until I started using more functions, then these hoisting issues started to arise and I couldn't understand why, and I just assumed I couldn't use the same variable name across functions, or that I was unable to reference variables outside of the same function, or that I had to force creation/using global variables using some weird method like this.variable or window.variable, or something else weird.

After I deleted all the places I had var except for the first one per variable, my issues disappeared.

The same thing applies to your code. If you change this:

var x = 'set';
var y = function () 
{
    if (!x) 
    { 
        var x = 'hoisted'; 
    }
    alert(x); 
}

y();

(changing var x = 'hoisted'; to x = 'hoisted';) to this:

var x = 'set';
var y = function () 
{
    if (!x) 
    { 
        x = 'hoisted'; 
    }
    alert(x); 
}

y();

then it works as you'd expect, and avoids hoisting.

1 Comment

If there is something wrong in my answer, please comment what it is instead of just downvoting. None of the other comments on this or any other answer (of which all also link to here) helped me understand this.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.