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.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
Grateful
  • 9,685
  • 10
  • 45
  • 77
  • 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 – dandavis Apr 16 '15 at 06:47
  • Yes, `var x` hoists to the top of the scope, this is normal behaviour of variable declaration in JS. – dfsq Apr 16 '15 at 06: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](http://stackoverflow.com/q/7506844/1048572)… – Bergi Apr 16 '15 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. – Qantas 94 Heavy Apr 17 '15 at 01:11

7 Answers7

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.

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
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.

dfsq
  • 191,768
  • 25
  • 236
  • 258
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.

Community
  • 1
  • 1
Crisoforo Gaspar
  • 3,740
  • 2
  • 21
  • 27
  • 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. – Qantas 94 Heavy Apr 17 '15 at 00:37
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);
    }
Goyal Vicky
  • 1,249
  • 16
  • 16
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();
GeniusGeek
  • 315
  • 6
  • 14
0

JavaScript only hoist declarations, not initializations.

-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.

gamingexpert13
  • 346
  • 5
  • 6
  • 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. – gamingexpert13 Apr 02 '21 at 20:01