'Hoisted' JavaScript Variables

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.


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.


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.