JavaScript has a curious behavior if you're coming from another programming language. (Okay, JavaScript has a lot of curious behaviors regardless of whether you're coming from another language.) In particular, there's a curios behavior around the scope of variable declarations.

Variables declared outside of any functions are global variables that can be referenced from any JavaScript code. Variables declared within a function are local variables and can only be referenced from with the function that declared them or from within scopes that are nested within the function that declared them.

var thisIsGlobal = "global";

function doSomething() {
    var thisIsLocal = "local";
    console.log(thisIsGlobal); // okay
    console.log(thisIsLocal);  // okay
}

console.log(thisIsGlobal); // okay
console.log(thisIsLocal);  // 'thisIsLocal' is not defined here

These rules are straight-forward and consistent with most programming languages. Here's where things can get confusing.

function monitorSleep() {
    console.log(sleepHours); // What happens here?
}

When you call this function, you'll see the following output in the console window:

Uncaught ReferenceError: sleepHours is not defined
   at monitorSleep (:2:17)
   at :1:1

That's understandable. We forgot to declare the sleepHours variable, so let's add it.

function monitorSleep() {
    console.log(sleepHours); // What happens here?

    var sleepHours = 0;
}

Now we've stumbled upon that curious behavior. We're referencing our sleepHours variable before we declare it, but instead of receiving the earlier error message, the console window reports:

undefined

If we duplicate the logging instruction after declaring the variable, we see this output:

undefined
0

Hoisting

This behavior is caused by hoisting. When JavaScript parses any code, the parser makes a first pass through the code and locates any variable or function declarations. Those declarations are then "hoisted" to the top of their current scope. So, let's say we have this code:

var a = 1;
var b = 2;

function doMath(num1, num2) {
    var sum = num1 + num2;
    console.log(sum);

    var diff = num1 - num2;
    console.log(diff);

    var prod = num1 * num2;
    console.log(prod);

    var quot = num1 / num2;
    console.log(quot);

    return Math.max(sum, diff, prod, quot);
}

var c = doMath(a, b);
console.log(c);

The JavaScript interpreter will understand it as:

var a = undefined;
var b = undefined;
var c = undefined;

function doMath(num1, num2) {
    var sum = undefined;
    var diff = undefined;
    var prod = undefined;
    var quot = undefined;

    sum = num1 + num2;
    console.log(sum);

    diff = num1 - num2;
    console.log(diff);

    prod = num1 * num2;
    console.log(prod);

    quot = num1 / num2;
    console.log(quot);

    return Math.max(sum, diff, prod, quot);
}

a = 1;
b = 2;

c = doMath(a, b);
console.log(c);

Notice all the variable declarations have been moved to the top of their scope, whether global or local, and initialized as undefined. This is why our earlier example sent undefined to the console window when we issued the console.log statement before declaring the variable. JavaScript had hoisted the declaration with an initial value of undefined.

let's Not Do This

ES6 introduced a new keyword for variable declarations: let. Variables declared with the let keyword don't participate in hoisting.

function monitorSleep() {
    console.log(sleepHours); // What happens here?

    let sleepHours = 8;
}

In this case, our example code generates an error indicating that sleepHours has not been defined when we reference it in the logging statement. sleepHours cannot be referenced until the let statement is executed.

What If We're strict About It?

You may be familiar with JavaScript's "strict" mode. We can enforce strict mode with the following statement:

"use strict";

We can place this statement at the top of our file to enforce strict mode throughout the file, or we can place it within a function to limit strict mode to the more narrow function scope. Strict mode requires that all variable have a formal declaration. Without strict mode, variables will implicitly be declared at the global scope.

function test() {
    nonstrict = 3;
}

test();
console.log(nonstrict);

Since strict mode is not enabled, the nonstrict variable is treated as though it had been declared using var at global scope:

var nonstrict = undefined;

function test() {
    nonstrict = 3;
}

test();
console.log(nonstrict);

However, it is worth noting that nonstrict is not available at global scope until the test function executes at least once and tries to assign a value to nonstrict. It's during the assignment operation that JavaScript recognizes the variable has not been declared and publishes it as a global variable.

function test() {
    nonstrict = 3;
}

// No call to test().
console.log(nonstrict); // 'nonstrict' has not been defined

Conclusion

In general, it is a good practice to use let instead of var to avoid unexpected problems. It is usually preferable to have JavaScript report an error rather than unexpectedly assign undefined to a hoisted variable.