JavaScript Function Declarations vs Function Expressions
Learn what function declarations and function expressions are and how they are similar/different with hoisting, blocks, scope, and global space pollution.
Table of Contents 📖
- Function Declaration
- Function Expression
- Hoisting
- Blocks and Function Declarations
- Global Space Pollution
Function Declaration
Function declarations start with the word function, similar to how variable declarations start with either var, let, or const.
function myDeclaration() { return "This a function declaration!"; }
Function declarations are functions with a name, here the name of the function is myDeclaration.
Function Expression
A function expression is a function that is part of an assignment expression. For example, capturing the function in a variable.
const myExpression = function myExpressionFunction() { return "This is a function expression!"; }
This is a named function expression as the function is given a name, myExpressionFunction. Named function expressions are only available inside the scope of the newly defined function.
const myExpression = function myExpressionFunction() { console.log(typeof myExpressionFunction); // function }
console.log(typeof myExpressionFunction); // undefined myExpression();
The main purpose of named function expressions is for debugging as they allow us to check the stack trace for the name of the function. Anonymous functions, or functions that have no name, are often used in function expressions.
const myExpression = function() { return "This is a function expression!"; }
However, function expressions are most often created with the ES6 arrow syntax.
const myExpression = () => { return "This is a function expression!"; }
Hoisting
Hoisting is when the JavaScript interpreter, prior to executing code, moves the declaration of functions, variables, and classes to the top of their scope. In other words, hoisting determines when functions, variables, and classes are accessible after being declared. Function declarations are hoisted, meaning we can call them before they are declared.
myDeclaration(); // No error, hoisted
function myDeclaration() { return "This a function declaration!"; }
Function expressions are not hoisted, meaning they can only be called after they have been declared.
myExpression(); // Error, not hoisted
const myExpression = () => { return "This is a function expression!"; }
Blocks and Function Declarations
Function declarations are not statements, meaning using them inside a block should be considered a syntax error. This is because historically, different browsers would parse these functions differently leading to inconsistencies.
// Different browsers will return different values. if (true) { function bad() { return true; } } else { function bad() { return false; } } bad();
This is because these blocks can only contain statements. However, we can use function expressions in blocks.
let good; if (true) { good = () => true; } else { good = () => false; } good();
However, the introduction of strict mode in ES5 resolved this issue by scoping function declarations to their block. To use strict mode, place use strict before any other statement.
"use strict"; { function badSyntax() { console.log("This isn't allowed in strict mode!"); } } badSyntax(); // ReferenceError
Global Space Pollution
When it comes to scope, function declarations are always local to the current scope.
// global scope function myGlobalFunction() {
// local scope
function myLocalFunction() {
}
}
We can use a function expression with the keywords let or const to avoid polluting the global namespace. Polluting the global scope is bad because we only want to have code accessible when and where we need it.
// const keyword doesn't pollute global namespace const noPollution = () => { console.log('No pollution!'); }
// let keyword doesn't pollute global namespace let alsoNoPollution = () => { console.log('Also no pollution!'); }
However, if we use a function expression with the var keyword in the outermost scope, it will still pollute the global namespace.
// var keyword in outermost scope will pollute global namespace var expressionPollution = () => { console.log('Expression pollution!'); }