Hoisting of JS variables declared without 'var'
Asked Answered
T

4

5

I am trying to get my head around hoisting and scopes in JavaScript, and am trying to figure out what exactly happens in this block of code. console.log(outside) and console.log(local) both log undefined, as I expected, as outside is declared but not initialised, and the declaration of local is hoisted to the top of the function. But why is typeof global equal to 'undefined'. Isn't omitting var inside a function the same as declaring the variable in global scope - in which case wouldn't it be hoisted?

var outside;
(function() {
    i = 2;
    if (i == 1) {
        var local = 'local';
        global = 'global';
    }
    // Demonstrates that local variables are hoisted but global variables are not.
    console.log(outside); // undefined
    console.log(local); // undefined
    console.log(global); // Uncaught ReferenceError: global is not defined. (i.e. typeof global === 'undefined')
})();

http://jsfiddle.net/ffjiang/sYvbL/

Towrope answered 23/7, 2014 at 5:23 Comment(0)
K
8

First off, only variables that are actually defined with var, let or const or function declarations are hoisted. var declarations are hoisted to the top of the function scope. const and let declarations are hoisted to the top of the containing block. While var declarations can be referenced before they are declared (in the same function), const and let declarations cannot be used before declaration (even though they are technically hoisted to the top of the block).

Assigning to a variable that has not been previously declared (when not running in strict mode) creates a global of that name at the moment that the assignment occurs. This is NEVER good programming to do that. You should always explicitly declare your variables. Even variables you intend to be globals should be declared globally.

In strict mode, you cannot do the "auto-global" thing where you just assign to a variable that doesn't exist and it automatically becomes a global. In Javascript, more and more pieces of code are automatically in strict mode now such as code that is part of a class declaration.

Trying to read a variable that has not been previously declared causes a reference error unless you prefix it with a containing object such as window.somevar in which case it would return the same as any other property of an object that doesn't yet exist, undefined.

Your code is basically equivalent to this (with one added console.log() statement):

var outside;      // declared as a global
(function() {
    var local;    // the declaration of this variable is hoisted to here
    i = 2;
    if (i == 1) {
        local = 'local';
        global = 'global';   // creates a global variable only when this line is executed
    }
    console.log(outside);        // undefined
    console.log(local);          // undefined
    console.log(window.global);  // undefined        
    console.log(global);         // Uncaught ReferenceError, no symbol of this name is found
})();

That means both outside and local are defined at the time you try to use them so there is no reference error. Neither was intialized so their value is undefined. global is not defined when you try to reference it because your assignment to it was not executed so it does not exist. There is no hoisting for the auto-creation of global variables when you don't actually declare them. Those variables are only created when and if the code that assigns to them is actually executed.

Kev answered 23/7, 2014 at 5:42 Comment(6)
Thanks, that makes a lot of sense :)Towrope
So to clarify, I thought that global = 'global' was both a declaration and assignment, but its actually just an assignment, which requires an on the spot declaration if executed.Towrope
Well. Actually variables declare with let also hoisted, the difference is the scope between var and let are difference...Primatology
@Primatology - Well let is hoisted only to the top of the block scope and you are not allowed to use the variable until after it's declaration so the hoisting is different in that way. With var, you can use it anywhere in the function regardless of where the declaration is.Kev
All declarations (variables and functions) are hoisted.Irremissible
@Irremissible - As this was a 2014 answer, I updated it a bit. The original answer was only about the OP's case of using var or not using var for variables in a function. Now, the answer is a bit more generic.Kev
M
2

Becouse you тot assigned a value to outside (it is undefined)

var outside = 5; // <=outside is 5
(function() {
    i = 2;

    if (i == 2) { // Condition is not satisfied. So variables are not declared. Replace to i==2;
        var local = 'local';
        global = 'global';
    }
    // Demonstrates that local variables are hoisted but global variables are not.
    console.log(outside); // 5
    console.log(local); // local
    console.log(global); // global
    return true
})();
Megan answered 23/7, 2014 at 5:31 Comment(4)
The condition not being satisfied was intentional. The idea was that those statements wouldn't be executed.Towrope
In this case, they will never be equal to anything but undefinedMegan
My question is why does console.log(global) give Uncaught ErrorReference and not the others?Towrope
becouse local is declared in code despite the fact that it does not explicitly declared(as var). But global explicitly undeclared. for example you can run this code: console.log(data1) console.log(data2) var data2 data1 is undefined, data2 = Error It strange, but it is a factMegan
Z
2

No hoisting of global takes place, because it's not declared using var inside the function body; only local is hoisted.

global = 'global';

Were that statement get run, it would implicitly create a global declaration if it didn't exist yet.

But it's not (run), and so it would rightfully raise a ReferenceError when you attempt to reference it.

Zacharie answered 23/7, 2014 at 5:38 Comment(0)
T
1

If you declare a variable with var, the declaration is propagated upwards to the closest scope (function definition in your case). Variables without var, however, are all implicitly assigned as properties of window object. So in your example global = 'global' is essentially the same as window.global = 'global'. Since the code never gets executed, window doesn't get this property and when you access it in console.log it is not defined.

Taper answered 23/7, 2014 at 5:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.