How to Declare and Call a JavaScript Function
Learn how to declare and call functions in JavaScript. Covers function declarations, function expressions, hoisting behavior, naming rules, the difference between defining and invoking, and common patterns for organizing function code.
Declaring a function means creating it. Calling a function means running it. These two steps are separate in JavaScript: you define what a function does, then you execute it whenever you need that behavior. Understanding this separation is essential because JavaScript treats declarations and expressions differently when it comes to hoisting, scope, and how your code loads.
This tutorial covers every way to declare functions in JavaScript, how to call them correctly, hoisting behavior that affects when functions become available, and practical patterns for organizing your function code.
Function Declarations
A function declaration uses the function keyword followed by a name, parentheses for parameters, and curly braces for the body:
function greet(name) {
return `Hello, ${name}!`;
}
const message = greet("Alice");
console.log(message); // "Hello, Alice!"Syntax Breakdown
function functionName(param1, param2) {
// function body
return value;
}| Component | Required | Description |
|---|---|---|
function keyword | Yes | Starts the declaration |
| Function name | Yes | Used to call the function |
Parameters () | Yes (even if empty) | Inputs the function receives |
Body {} | Yes | Code that executes on each call |
return statement | No | Sends a value back to the caller |
Hoisting: Call Before Declaration
Function declarations are hoisted. JavaScript moves them to the top of their scope before running any code, so you can call them before the line where they appear:
// This works because of hoisting
const result = add(3, 5);
console.log(result); // 8
function add(a, b) {
return a + b;
}// Behind the scenes, JavaScript sees it as:
function add(a, b) {
return a + b;
}
const result = add(3, 5);
console.log(result); // 8Hoisting Only Applies to Declarations
Only function declarations are hoisted. Function expressions and arrow functions assigned to variables follow the hoisting rules of their variable keyword (let, const, var). A const function expression is not accessible before its definition line.
Function Expressions
A function expression creates a function and assigns it to a variable:
const greet = function (name) {
return `Hello, ${name}!`;
};
console.log(greet("Bob")); // "Hello, Bob!"The function itself has no name (it is anonymous). The variable greet holds a reference to the function.
Named Function Expressions
You can give the function a name even in an expression. The name is only accessible inside the function body (useful for recursion and debugging):
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // 'fact' is accessible here
};
console.log(factorial(5)); // 120
// console.log(fact(5)); // ReferenceError: fact is not definedNo Hoisting for Expressions
// This throws an error
// console.log(multiply(3, 4)); // ReferenceError: Cannot access 'multiply' before initialization
const multiply = function (a, b) {
return a * b;
};
console.log(multiply(3, 4)); // 12How to Call a Function
Basic Call
Add parentheses after the function name to execute it:
function sayHello() {
console.log("Hello!");
}
sayHello(); // Hello!Passing Arguments
Arguments go inside the parentheses, separated by commas:
function formatPrice(amount, currency) {
return `${currency}${amount.toFixed(2)}`;
}
console.log(formatPrice(29.9, "$")); // "$29.90"
console.log(formatPrice(1499, "€")); // "€1499.00"Storing the Return Value
function getFullName(first, last) {
return `${first} ${last}`;
}
// Store the result in a variable
const name = getFullName("John", "Doe");
console.log(name); // "John Doe"
// Use directly in another expression
console.log(getFullName("Jane", "Smith").toUpperCase()); // "JANE SMITH"
// Pass as argument to another function
console.log(getFullName("Bob", "Wilson").length); // 10Calling Without Arguments
If a function expects parameters but you call it without arguments, the parameters become undefined:
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet()); // "Hello, undefined!"
console.log(greet("Alice")); // "Hello, Alice!"Declaration vs Expression Comparison
| Feature | Declaration | Expression |
|---|---|---|
| Syntax | function name() {} | const name = function() {} |
| Hoisted | Yes (fully hoisted) | No (follows variable rules) |
| Named | Always | Optional |
| Semicolon after | No | Yes (it is an assignment) |
| Use before definition | Yes | No |
| Overwritable | Can be redeclared in same scope | const prevents reassignment |
| Debugging | Name shown in stack traces | Variable name or "anonymous" |
When to Use Each
// Declaration: general-purpose functions
// Good when hoisting is useful or function is a primary building block
function calculateTax(price, rate) {
return price * rate;
}
// Expression: callbacks, conditional functions, module patterns
// Good when you want to prevent hoisting or assign conditionally
const calculateDiscount = function (price, percent) {
return price * (percent / 100);
};Calling Functions Inside Functions
Functions can call other functions. This is how you build complex behavior from simple pieces:
function isValidAge(age) {
return typeof age === "number" && age >= 0 && age <= 150;
}
function isValidName(name) {
return typeof name === "string" && name.trim().length > 0;
}
function validateUser(name, age) {
const errors = [];
if (!isValidName(name)) {
errors.push("Name is required");
}
if (!isValidAge(age)) {
errors.push("Age must be a number between 0 and 150");
}
return {
valid: errors.length === 0,
errors: errors,
};
}
console.log(validateUser("Alice", 28));
// { valid: true, errors: [] }
console.log(validateUser("", -5));
// { valid: false, errors: ["Name is required", "Age must be a number between 0 and 150"] }Functions with Multiple Return Points
Functions can have multiple return statements. Each acts as an exit point:
function getLetterGrade(score) {
if (score >= 90) return "A";
if (score >= 80) return "B";
if (score >= 70) return "C";
if (score >= 60) return "D";
return "F";
}
console.log(getLetterGrade(95)); // "A"
console.log(getLetterGrade(73)); // "C"
console.log(getLetterGrade(42)); // "F"This pattern works well with if statements as guard clauses. Each condition checks and exits early, making the logic linear and easy to follow.
Immediately Invoked Function Expressions (IIFE)
You can define and call a function in one step by wrapping it in parentheses and adding ():
(function () {
const secret = "hidden";
console.log("This runs immediately!");
// secret is not accessible outside
})();
// Named IIFE
(function setup() {
console.log("Initializing...");
})();IIFEs create a private scope. Variables inside them do not leak into the global scope. This pattern was essential before ES6 modules, and it is still useful for one-time initialization scripts.
Practical Examples
Form Validator
function validateEmail(email) {
if (typeof email !== "string") return false;
if (email.length === 0) return false;
if (!email.includes("@")) return false;
if (!email.includes(".")) return false;
return true;
}
function validatePassword(password) {
if (typeof password !== "string") return false;
if (password.length < 8) return false;
return true;
}
function validateForm(email, password) {
const result = { valid: true, messages: [] };
if (!validateEmail(email)) {
result.valid = false;
result.messages.push("Please enter a valid email");
}
if (!validatePassword(password)) {
result.valid = false;
result.messages.push("Password must be at least 8 characters");
}
return result;
}
console.log(validateForm("user@example.com", "secure123"));
// { valid: true, messages: [] }
console.log(validateForm("invalid", "short"));
// { valid: false, messages: ["Please enter a valid email", "Password must be at least 8 characters"] }Temperature Converter
function celsiusToFahrenheit(celsius) {
return (celsius * 9) / 5 + 32;
}
function fahrenheitToCelsius(fahrenheit) {
return ((fahrenheit - 32) * 5) / 9;
}
function formatTemperature(value, unit) {
return `${value.toFixed(1)}°${unit}`;
}
const boiling = celsiusToFahrenheit(100);
console.log(formatTemperature(boiling, "F")); // "212.0°F"
const body = fahrenheitToCelsius(98.6);
console.log(formatTemperature(body, "C")); // "37.0°C"Common Mistakes
Missing Parentheses
function getTimestamp() {
return Date.now();
}
// Bug: assigns the function itself, not its result
const time = getTimestamp;
console.log(time); // [Function: getTimestamp]
// Fix: call the function
const time = getTimestamp();
console.log(time); // 1709510400000Declaring Inside a Loop Unnecessarily
// Wasteful: creates a new function on every iteration
for (let i = 0; i < 100; i++) {
function process(val) {
return val * 2;
}
console.log(process(i));
}
// Better: declare once, call many times
function process(val) {
return val * 2;
}
for (let i = 0; i < 100; i++) {
console.log(process(i));
}One Function, One Job
If a function does more than one distinct thing (validates data AND sends an email AND updates the database), split it into separate functions. Single-responsibility functions are easier to test, debug, and reuse.
Rune AI
Key Insights
- Declarations use
function name()and are fully hoisted: callable before their definition line - Expressions use
const name = function()and are not hoisted: must be defined before use - Call with parentheses, reference without:
greet()runs the function,greetpasses it as a value - Multiple return points work as guard clauses: check conditions early and exit, keeping logic flat
- Each function should do one thing: single-responsibility functions are easier to test and reuse
Frequently Asked Questions
Should I use function declarations or expressions?
Can I redeclare a function with the same name?
What does "calling" vs "referencing" a function mean?
How many functions should a file have?
Is there a performance difference between declarations and expressions?
Conclusion
Declaring a function stores a reusable block of code. Calling a function executes it. Function declarations are hoisted (available before their definition line), while function expressions follow variable scoping rules and are not hoisted. Both create the same kind of function object under the hood. Use declarations for top-level building blocks and expressions for callbacks, conditional assignments, and cases where you want const to prevent reassignment. Structure your code so each function does one thing well, name it clearly, and compose small functions into larger operations.
More in this topic
OffscreenCanvas API in JS for UI Performance
Master the OffscreenCanvas API to offload rendering from the main thread. Covers worker-based 2D and WebGL rendering, animation loops inside workers, bitmap transfer, double buffering, chart rendering pipelines, image processing, and performance measurement strategies.
Advanced Web Workers for High Performance JS
Master Web Workers for truly parallel JavaScript execution. Covers dedicated and shared workers, structured cloning, transferable objects, SharedArrayBuffer with Atomics, worker pools, task scheduling, Comlink RPC patterns, module workers, and performance profiling strategies.
JavaScript Macros and Abstract Code Generation
Master JavaScript code generation techniques for compile-time and runtime metaprogramming. Covers AST manipulation, Babel plugin authorship, tagged template literals as macros, code generation pipelines, source-to-source transformation, compile-time evaluation, and safe eval alternatives.