Ways to Declare Functions in JavaScript: Technical and Practical Guide
Traditional function declaration (function declaration)
This is the most classic form and is defined using the function keyword followed by a mandatory name. One of its most distinctive technical features is hoisting, which allows you to call the function in lines of code that appear before its physical definition in the file.
Example with parameters:
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("World")); // "Hello, World!"Function expression
In this approach, the function is treated as a data value that is assigned to a variable. You can use let, var or const to declare that variable. Unlike declarations, these are not fully subject to hoisting; the function does not exist until execution reaches the assignment line.
- Anonymous: It has no explicit name.
- Named: It includes an internal identifier useful for recursion and debugging (it appears in the stack trace).
Example:
const sum = function(a, b) {
return a + b;
}; // Using 'const' helps avoid accidental reassignmentsArrow functions
Introduced in ES6, arrow functions offer a very compact syntax and remove the need for the function keyword. Their biggest semantic difference is that they do not have their own this binding; instead, they capture this from the surrounding lexical scope.
- Short syntax: If they have only one parameter and one line of code, you can omit parentheses, braces and the explicit
return.
Example:
const square = x => x * x; // Implicit returnMethod definition in objects and classes (shorthand)
This is a shorthand syntax for declaring functions inside object literals or classes, omitting the colon and the function keyword. These methods are the only ones that can access the super binding.
Example in an object:
const calculator = {
value: 10,
double() {
return this.value * 2;
} // Clean syntax
};Async functions (async)
Marked with the async prefix, these functions always return a Promise. They allow the use of await inside their body to pause execution in a non-blocking way until a promise is resolved.
Example:
async function fetchData() {
const response = await fetch('url');
return response.json();
}Generator functions (function*)
They are defined with function* and allow non-continuous execution; they can pause their state and yield control back to the caller using the yield keyword.
Example:
function* counter() {
yield 1;
yield 2;
}Immediately Invoked Function Expressions (IIFE)
These are functions that run as soon as they are defined. They are mainly used to create a private scope and avoid polluting the global namespace.
Example:
(function() {
const message = "Private";
console.log(message);
})();Function constructor (not recommended)
The Function constructor lets you create functions dynamically from strings at runtime. Its use is discouraged due to security risks and poor optimization by JavaScript engines.
Example:
const dynamicSum = new Function('a', 'b', 'return a + b');Analogy to understand the differences
Imagine a function declaration as a recipe carved in stone in the kitchen: it has always been there and everyone knows where to find it even before the shift starts. A function expression is like a sticky note you write on the spot: you can only follow its instructions after you have written it and stuck it to the wall. Finally, an arrow function is like a mental shortcut that uses the context of what you are already doing to complete a quick task without needing a full kitchen.