JavaScript before executing your code parses it, and adds to its own memory every function and variable declarations it finds, and holds them in memory. This is called hoisting.

We have some different behaviors for function declarations and function expressions.

With function declarations, we can call a function before it’s defined, and our code will work. In the other cases, we’ll have errors.

A general rule of thumb is to always define functions, variables, objects and classes before using them, to avoid surprises.

Suppose we have a function:

function bark() {
}


Due to hoisting, we can technically invoke bark() before it is declared:

bark()
function bark() {
}


With functions, this only happens for function declarations. Like in the case above.

Not with function expressions.

This is a function expression:

bark()
var bark = function() {
}


In this case, the var declaration is hoisted and initialized with undefined as a value, something like this:

var bark = undefined
bark()
bark = function() {
}


Running this code will give you a TypeError: bark is not a function error.

const and let declarations are hoisted, too, but they are not initialized to undefined like var.

const bark = function() {
}


or

let bark = function bark() {

In this case, if you invoke bark() before declaring it, it will give you a ReferenceError: Cannot access 'bark' before initialization error.
Class declarations work like let and const declarations: they are hoisted, but not initialized, and using a class before its declaration will give a ReferenceError: <Class> is not defined error.