Functions
Function is a subroutine, an independent piece of code designed to repeatedly execute a specific task with different initial values. Functions help structure massive programs, reduce repetition and isolate code.
A function can be thought of as a black box, it receives something at the input (data) and returns something at the output (the result of code execution inside).

Function declaration
// 1. Multiply function declaration
function multiply() {
// Function body
console.log("Multiply function log");
}
// 2. Multiply function calls
multiply(); // 'Multiply function log'
multiply(); // 'Multiply function log'
multiply(); // 'Multiply function log'
Function declaration begins with the keyword function
, followed by its
name, i.e. a verb answering the question "What to do?", and a pair of
parentheses.
The function body is enclosed in curly braces {}
and contains instructions
that must be executed when calling it. Then, when necessary, the function is
called with a name and a pair of parentheses.
Parameters and arguments
In parentheses, after the function name, there are parameters, an enumeration of data that the function expects when called.
// Declaration of parameters x, y, z
function multiply(x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
}
Parameters are local variables available only in the function body. They are separated by commas. There may be several parameters, or, if none at all, empty parentheses are used.
Parameters will be recreated each time the function is called, and their individual incarnations are not interrelated.
When calling a function, in parentheses you can pass arguments, i.e. values for the declared function parameters.
// 1. Declaration of parameters x, y, z
function multiply(x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
}
// 2. Passing arguments
multiply(2, 3, 5); // Multiplication result is 30
multiply(4, 8, 12); // Multiplication result is 384
multiply(17, 6, 25); // Multiplication result is 2550
The order of passing arguments must match the order of the declared parameters:
the value of the first argument will be assigned to the first parameter, the
second argument will be assigned to the second parameter, etc. If there are more
parameters than arguments, then parameters without values will be assigned
undefined
.
Value return
The return
operator is used to pass a value from the function body to external
code. When the interpreter sees return
, it immediately exits the function
(terminates its execution), and then it returns the specified value to the part
of the code where the function is called.
function multiply(x, y, z) {
console.log("The code before return is executed as usual");
// Returning the result of the multiplication expression
return x * y * z;
console.log(
"This log will never be executed, as it is positioned after return"
);
}
// The function result can be saved to a variable
let result = multiply(2, 3, 5);
console.log(result); // 30
result = multiply(4, 8, 12);
console.log(result); // 384
result = multiply(17, 6, 25);
console.log(result); // 2550
Without an explicitly specified value, the return
statement returns the
special value undefined
. If there is no return
in the function body, it will
still return undefined
.
Code execution order
When the interpreter sees a function (or method) call, it pauses the execution of the current code and starts executing the code from the function body. After all the function code has been executed, the interpreter exits the function body, returns to the place where it came from and continues to execute the code following the function call.
function multiply(x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
}
console.log("Log before calling the multiply function");
multiply(2, 3, 5); // Multiplication result is 30
console.log("Log after calling the multiply function");
// Sequence of logs in the console:
// "Log before calling the multiply function"
// "Multiplication result is 30"
// "Log after calling the multiply function"
Default parameters
Sometimes you need to declare a function with parameters other than undefined
,
even if no arguments have been passed for them. This is done in a very simple
and obvious way: it is enough to specify the default value when declaring
parameters in the function signature. With this notation, if no argument value
has been passed for the parameter, the default value is used.
function count(countFrom = 0, countTo = 10, step = 1) {
console.log(`countFrom = ${countFrom}, countTo = ${countTo}, step = ${step}`);
for (let i = countFrom; i <= countTo; i += step) {
console.log(i);
}
}
count(1, 5); // countFrom = 1, countTo = 5, step = 1
count(2); // countFrom = 2, countTo = 10, step = 1
count(); // countFrom = 0, countTo = 10, step = 1
arguments
pseudo-array
The list of all arguments can be accessed using the special variable
arguments
, which is only available inside the function and stores all
arguments as a pseudo-array.
Pseudo-array is a collection with the length
property and the ability to
access an element by index, but it lacks most methods for working with arrays.
Consider an example of using arguments
in a function that multiplies any
number of arguments:
function multiply() {
let total = 1;
for (const argument of arguments) {
total *= argument;
}
return total;
}
console.log(multiply(1, 2, 3)); // 6
console.log(multiply(1, 2, 3, 4)); // 24
console.log(multiply(1, 2, 3, 4, 5)); // 120
Pseudo-array conversion
Usually pseudo-arrays need to be converted to standard arrays, since
pseudo-arrays lack array methods such as slice()
or includes()
. In practice,
several basic methods are used.
Using the Array.from()
method will create an array from a pseudo-array.
function fn() {
// The args variable will contain a standard array
const args = Array.from(arguments);
}
Using the ...
(rest) operation makes it possible to collect an arbitrary
number of elements, in our case arguments, inside an array and store it in a
variable. Collect all the arguments using the rest
operation right in the
function signature.
function fn(...args) {
// The args variable will contain a standard array
}
The rest
operation will be covered in more detail later; here you can see just
one of its possible uses.
"Return early" pattern
The if...else
statement is the main branching method. However, complex nested
branches make your code confusing.
Let's create a function to process withdrawals from a personal bank account. It receives the amount to be withdrawn and the current account balance, after which, depending on the condition, executes a certain block of code.
function withdraw(amount, balance) {
if (amount === 0) {
console.log("For the transaction, enter an amount greater than zero");
} else if (amount > balance) {
console.log("Insufficient account balance");
} else {
console.log("The withdrawal has been successful");
}
}
withdraw(0, 300); // "For the transaction, enter an amount greater than zero"
withdraw(500, 300); // "Insufficient account balance"
withdraw(100, 300); // "The withdrawal has been successful"
Even in such a simple example, there is a group of nested conditional operators, and the code execution logic may not be obvious.
There can be more than one return
statement in a function. The main thing to
remember is that the execution of a function is interrupted when the interpreter
sees a return, and all the code after it will be ignored in the current function
call.
Return early pattern is a way to return early from a function using the
return
operator. With this technique, you get cleaner, flatter and more
understandable code that is easier to refactor.
Separate all condition checks into separate if
statements, then add code in
the else
body. Ideally, you should end up with a flat list of conditional
statements, following each other, and at the end a block that will be executed
only when no if
can be executed.
function withdraw(amount, balance) {
// If the condition is met, console.log is called
// and the function exited. The code following the if body will not be executed.
if (amount === 0) {
console.log("For the transaction, enter an amount greater than zero");
return;
}
// If the condition of the first if is not met, its body is skipped,
// and the interpreter goes to the second if.
// If the condition is met, console.log is called and the function exited.
// The code following the if body will not be executed.
if (amount > balance) {
console.log("Insufficient account balance");
return;
}
// If none of the previous ifs have been executed,
// the interpreter reaches this code and executes it.
console.log("The withdrawal has been successful");
}
withdraw(0, 300); // "For the transaction, enter an amount greater than zero"
withdraw(500, 300); // "Insufficient account balance"
withdraw(100, 300); // "The withdrawal has been successful"
Function expression
Function expression is a standard declaration of a variable with a function as its value. This is an alternative way to declare functions.
// Function declaration
function multiply(x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
}
// Function expression
const multiply = function (x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
};
The difference is that a function expression cannot be called before its
creation, only after, as it is literally a const
variable declaration.
// ❌ Error! Calling before declaration
multiply(1, 2, 3);
const multiply = function (x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
};
// ✅ Calling after declaration
multiply(4, 5, 6);
Whereas a function declaration can be called before its position in the code.
// ✅ Calling before declaration
multiply(1, 2, 3);
function multiply(x, y, z) {
console.log(`Multiplication result is ${x * y * z}`);
}
// ✅ Calling after declaration
multiply(4, 5, 6);
It does not matter what syntax you use, the main thing is to make the project code homogeneous. Try not to mix function declarations with function expressions.