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.