What is the difference between a function declaration function myVar() {}
and a function expression var myVar = function() {};
?
In order to properly understand the (subtle) differences between a function declaration and a function expression we'll need to dive into the inner workings of JavaScript a little bit.
Consider this:
console.log(myVar); // ReferenceError: myVar is not defined
As expected, a ReferenceError
is thrown as myVar
is not defined. Now consider the following code block:
console.log(myVar); // undefined
var myVar = 'John likes ponies.';
console.log(myVar); // 'John likes ponies.'
Strangely the first console.log did not throw a ReferenceError
as one might expect; it was undefined. This is because JavaScript has "hoisting." Hoisting basically looks ahead and hoists all of the variables to the top of scope. myVar
was hoisted to the top of the scope because it was declared somewhere down the sequence. Here's what actually ended up happening despite the structure of our last code block:
var myVar;
console.log(myVar); // undefined
var myVar = 'John likes ponies.';
console.log(myVar); // 'John likes ponies.'
Knowing that let's look at how a function expression could be effected by hoisting:
console.log(myFunc()); // ReferenceError: myFunc is not defined
As expected myFunc()
throws a ReferenceError
just as myVar
did in our first code block. But what happens when we leverage JavaScript hoisting by writing a function expression after our log statement?
console.log(myFunc()); // TypeError: undefined is not a function
var myFunc = function () {
return 'John likes ponies';
};
Even though we have created a function expression to define myFunc
AND myFunc
effectively gets hoisted above our console.log
statement the result of our log is still a TypeError
. myFunc
may be hoisted, however, its value is still undefined and it cannot be called as a function until after our function expression statement. With hoisting this is what our previous code block actually looks like:
var myFunc;
console.log(myFunc()); // TypeError: undefined is not a function
myFunc = function () {
return 'John likes ponies';
};
However, function declarations behave differently because they also have hoisting. Declared functions are accessible anywhere within the function scope. This is a valid example:
console.log(myFunc()); // 'John likes ponies'
function myFunc() {
return 'John likes ponies';
};
It's easy to see how relying on hoisting with function expressions and function declarations can be tricky, especially for someone newer to JavaScript. I avoid relying on hoisting using these two rules.
- All of my
var
statements go at the top of the scope. (with the exception of avar
statement in afor
loop initialization) - All of my named functions are function declarations immediately after my
var
statements. (with the exception of the occasional anonymous callback function)
Enforcing these guidelines is useful for developers at all levels being that it better represents how the code is actually interpreted.
So how does a function expression differ from a function declaration? It all depends on context and use. Generally speaking you should follow a pattern that is as true to the runtime version of the code as possible.