- 3.1 Declaring Functions
- 3.2 Higher-Order Functions
- 3.3 Function Literals
- 3.4 Arrow Functions
- 3.5 Functional Array Processing
- 3.6 Closures
- 3.7 Hard Objects
- 3.8 Strict Mode
- 3.9 Testing Argument Types
- 3.10 Supplying More or Fewer Arguments
- 3.11 Default Arguments
- 3.12 Rest Parameters and the Spread Operator
- 3.13 Simulating Named Arguments with Destructuring
- 3.14 Hoisting
- 3.15 Throwing Exceptions
- 3.16 Catching Exceptions
- 3.17 The finally Clause
- Exercises
3.14 Hoisting
In this “mad hatter” section, we take up another complex subject that you can easily avoid by following three simple rules. They are:
Don’t use var
Use strict mode
Declare variables and functions before using them
If you want to understand what happens when you don’t follow these rules, read on.
JavaScript has an unusual mechanism for determining the scope of a variable—that is, is the region of a program where the variable can be accessed. Consider a local variable, declared inside a function. In programming languages such as Java, C#, or C++, the scope extends from the point where the variable is declared until the end of the enclosing block. In JavaScript, a local variable declared with let appears to have the same behavior:
function doStuff() { // Start of block . . . // Attempting to access someVariable throws a ReferenceError let someVariable // Scope starts here . . . // Can access someVariable, value is undefined someVariable = 42 . . . // Can access someVariable, value is 42 } // End of block, scope ends here
However, it is not quite so simple. You can access local variables in functions whose declarations precede the variable declaration:
function doStuff() { function localWork() { console.log(someVariable) // OK to access variable . . . } let someVariable = 42 localWork() // Prints 42 }
In JavaScript, every declaration is hoisted to the top of its scope. That is, the variable or function is known to exist even before its declaration, and space is reserved to hold its value.
Inside a nested function, you can reference hoisted variables or functions. Consider the localWork function in the preceding example. The function knows the location of someVariable because it is hoisted to the top of the body of doStuff, even though that variable is declared after the function.
Of course, it can then happen that you access a variable before executing the statement that declares it. With let and const declarations, accessing a variable before it is declared throws a ReferenceError. The variable is in the “temporal dead zone” until its declaration is executed.
However, if a variable is declared with the archaic var keyword, then its value is simply undefined until the variable is initialized.
Since functions are hoisted, you can call a function before it is declared. In particularly, you can declare mutually recursive functions:
function isEven(n) { return n === 0 ? true : isOdd(n -1) } function isOdd(n) { return n === 0 ? false : isEven(n -1) }
As long as you use strict mode and avoid var declarations, the hoisting behavior is unlikely to result in programming errors. However, it is a good idea to structure your code so that you declare variables and functions before they are used.