We can do a lot with JavaScript, but this means we can end up writing a lot of code. However, we don’t want to waste time. We want to organize our code and not repeat some parts of it multiple times. That’s where functions come in. In this JavaScript tutorial, we will see everything that you need to know about functions in JS.
Here we talk specifically about functions in JavaScript. This means you need to grasp the basics of it to get the most out of this tutorial. In case you are completely new to JavaScript, you should start with this Getting Started tutorial.
What is a Function in JavaScript?
Functions are like assembly-lines
Whenever we talk about functions, I like to describe them as an automated assembly line. Imagine an assembly line in a factory, exclusively made of robots. Some products come in, and a finished (or assembled) product comes out.
For example, think about toothpaste. Each tube has three components: the tube, the toothpaste itself, and the cap. An assembly line may work like this: a robot squeezes the tootphaste into the tube, then another puts the cap on top.
Yet, the robots in this assembly line are dumb. They don’t know what they are doing, they are just “muscles”. As such, if you were to put the tubes or the caps not in the right position, they wouldn’t be able to pick them up.
Functions take some information in, elaborate it, and then give you back a result.
Functions are just like assembly lines, but with information – not with physical products. They take some information in, elaborate on it, and then give you back a result.
Functions are not something specific to JavaScript. Every modern programming language has them, as they are crucial to write good and scalable code.
The Anatomy of a Function in JavaScript
To work with functions, we need to understand how to define and use them first. In fact, with a function you have to do basically two things.
- First, you define the function. This means creating the assembly line itself, defining what are the operations that the function will perform. In other words, how it will get from the input information to the final result. Don’t be scared – we do by writing “simple” JavaScript code.
- Then, wherever we want in our code, we can call the function. Doing that means feeding it with some data and getting the final result from their elaboration.
In doing that, we need to keep in mind the functions jargon. Indeed, each function has a name, much like a variable. You decide it when you define the function, and then use it whenever you call it. Then, each function has a code block, containing the operation it needs to perform.
On top of that, functions tend to have some return value. This is the information that will be calculated and given back after the elaboration. If you don’t specify one, this value will always be undefined
.
Furthermore, functions can have zero or more parameters (or arguments). Those are pieces of information that the function expects as input to do the calculation. With that in mind, we can create a function named add
that will sum two numbers, a
and b
, and return them back.
// Define the function
function add(a, b) {
return a + b;
}
// Call the function
add(1, 2); // 3
add(0, 0); // 0
add(-10, 43); // 33;
5 + add(2, 3); // 10
Let’s explore the syntax now.
JavaScript Functions Syntax
To define a function, we need to use the keyword function, followed by the name we want to give to our function. Then, between brackets, we need to list all the parameters we want to use (if none, simply open and close brackets such as ()
). After that, we define a code block containing the code that our function will execute.
In that code, we can use the return
keyword followed by a value (it doesn’t have to be static). Whenever the return statement is reached, the function will terminate its execution and will give back the return value. The function may end up not hitting the return statement (for example if it is in an if) and finish without that. In that case, it implicitly returns undefined
.
The parameters you define in the brackets are simply placeholders, much like variables. However, they are a special type of variable, because they exist only within the function. Whenever you call the function, you will have to feed those parameters, and those will go in order.
So, if we have a function that we define as function test(a, b, c, d)
and we call it with test(10, 20, -1, 'c')
then, when the function executed, a
will be 10
, b
will be 20
, c
will be -1
and d
will be the string c
.
You can pass variables to function parameters, and the names don’t have to match the ones in the arguments. The following example can clarify that.
function multiply(a, b) {
return a * b;
}
let a = 20;
let other = 2;
let final = multiply(other, a);
// final will be 40 now
console.log(final)
Functions and Parameters
Before diving any deeper, we should spend some more words on function parameters, or arguments. Much like fight club, whatever happens in the function remains in the function. The only thing going out is the return value. That’s to say that any modification you do to the parameters is not reflected to the outside of the function, if you passed a variable as parameter.
The following example will help you clarify this.
function uselessFunction(a, b) {
a = 4;
return a * 2 + b;
}
let n1 = 10;
let n2 = 2;
let n3 = uselessFunction(n1, n2);
console.log(n1); // 10: the modification a = 4 remained within the function
console.log(n2); // 2: never touched
console.log(n3); // 10: 4 * 2 + 2
This is important because it easily allows us to do calculations and write cleaner code without risking unwanted changes. However, that is not to say that the parameters cannot be changed in any case. In fact, that’s only true with simple types that can only be assigned.
Any operation that is not an assignment will be reflected in the outside world. For example, if your parameter is an array, and you alter with a push()
operation, this will be reflected on the outside.
function pushOne(arr) {
arr.push(1);
}
let test = [1, 2, 3, 4];
pushOne(test);
console.log(test); // [1, 2, 3, 4, 1]
However, reassignment doesn’t affect the outside world even with arrays (e.g. arr = []
).
How and Why Use Functions
Benefits of Functions
The most evident benefit of functions is that you can have a quick short-hand. With just one line of code, you can reference a much complex operation that takes many times.
Thus, this enables also more reusability. You can quickly plug complex operations wherever you want in the code. On top of that, by updating the function’s definition you can quickly update how it works wherever it is called. If variables abstract data, functions abstract logic.
All of that translates into writing better code, code that is more reusable and easier to maintain and update. In other words, it enables you to work faster. Yet, to be able to do that, using functions is not enough. We need to use them in a proper way, following some best practices. Let’s see them.
Functions’ Best Practices
The most important thing you have to remember is that each function must have a single purpose. Doing multiple things in the same function is a bad idea. Why? Because at some point you may want to decouple tasks you used to do together. When that happens, it will be easier if you already have them in separate functions.
Also remember that when you want to update a function, it’s much easier if it’s code is clear and simple, that’s something you can achieve with a single purpose. Ideally, your functions should have 10-30 lines of code in them at most. There is no actual rule for that, but the less the better. Also, what happens within a function should be at the same abstraction level.
Imagine you are tasked with the construction of an airplane. You can have some very high-level tasks, such as “attaching the wings to the body”, which in turn are made of very detailed tasks, such as “screwing bolt X in position Y”. All the code in a function should be at the same detail level, or abstraction level.
So, in our airplane example, we may put in our createAirplane()
function the functions attachLeftWing()
and attachRightWing()
, but not screwBoltX()
. That may go inside attachLeftWing()
for example.
Functions Can Call Other Functions
Something quite nice about functions is that you can nest them at many levels. In fact, a function can rely on another function to work. For example, our power()
function can rely on the multiply()
function as below.
function multiply(a, b) {
return a * b;
}
function power(base, exp) {
if (exp === 0) return 1;
if (exp < 0) return undefined; // Not implemented, we return undefined
let result = base;
// We start from 1 so that x^1 = x
for(let i = 1; i < exp; i++) {
result = multiply(result, base);
}
return result;
}
We can go even beyond that. In fact, a function can call other functions, but it can also call itself. We call this recursion, as we go again and again inside the same function. However, we have to be careful there because if we don’t have some mechanism to terminate we will create an infinite recursion and our app will crash. So, if we want to use recursion, we need to implement it with a sound if-else strategy.
function recursiveFactorial(n) {
if (n <= 1) return n;
return recursiveFactorial(n-1) * n;
}
// 5! = 120
console.log(recursiveFactorial(5)); // 120
As you can see in this example, we continue to call recursiveFactorial()
over and over, always decrementing the value by 1
. However, as soon as this value reaches 1, we stop with recursion and we simply return n
(which will be 1
). Going back, JavaScript will resolve all the calculations and finally return the result.
No Return Value, or No Parameters
Sometimes, you may want to create a function that has no return value or no parameters. Why would you do that? What kind of elaboration can you do in such cases?
A function with no return value is often useful when data is going somewhere else. For example, if you want to enrich some log information (that is, to add some information to a log) and print it out, you don’t need to return it. Think about adding date and time to a log message.
Instead, a function with no parameters may get is initial data somewhere else. A common use case is when you want to load some information from the web, for example weather forecast. You don’t need to provide much, the function will just connect to the website and get the data.
Another use you may see for functions with no parameters is when you want to create a constant. In other words, a function that always reutrn the same value, so that we are sure that it cannot be changed or altered (unlike a variable).
JavaScript Functions for Full Stack Developers
This JavaScript tutorial is part of a Full Stack Course where we teach you how to become a full stack developer. Most of that, as you know, comes from practice. Functions make no exception, and you should practice a lot with them.
To help you start your practice, we propose an exercise you can do. You can find this as part of our Pretend Bakery Website project (available here on GitHub.com). However, as we do this exercise as standalone, you could do it potentially just with a javascript file.
The Assignment
For this exercise, you should use functions to take an array as input and return another array as output, containing three elements: the minimum value, the maximum value, and the average value. We can call our function calc(arr)
.
As part of this exercise, you can consider it safe to assume that the input array will always contain numbers (not necessarily positive or negative). However, you need to consider that the input array may be empty. In case the array is empty, we should return undefined instead of an array.
To consider some example input and output values, consider the following.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] --> [0, 9, 4.5]
[10] --> [10, 10, 10]
[-1, -40, 23, 12] --> [-40, 23, -1.5]
[] --> undefined
Try implementing a solution to this problem on your own before looking at the solution. Then, once you feel confident or stuck, read on.
The Solution
Alright, so here is the solution to the assignment. Be sure to continue reading only if you tried for yourself first!
As we know from the assignment, we need to obtain three different values from the same input: the minimum, the maximum, and the average. Since we follow the best practices, we should consider that each function should have a single purpose. Because of that, we need to create three functions to calculate these three values, and then glue them together in our calc()
function.
So, we start with the function to calculate the minimum, which we call min(arr)
. Note that we do not consider the possibility that the array is empty here. That’s because we can deal with that once in our calc(arr)
function for all our three other functions.
function min(arr) {
let min = arr[0];
for(let i = 0; i < arr.length; i++) {
if (arr[i] < min) min = arr[i];
}
return min;
}
Then, we go on with max(arr)
.
function max(arr) {
let max = arr[0];
for(let i = 0; i < arr.length; i++) {
if (arr[i] > max) max = arr[i];
}
return max;
}
And, after that, we define avg(arr)
.
function avg(arr) {
let sum = 0;
for (let i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum / arr.length;
}
Now, we can finally create calc(arr)
. Here, the first thing we want to do is check the length of the array. If it’s zero, and thus the array is empty, we return undefined. Otherwise, we compose a new array on-the-fly containing the result of our three other functions.
function calc(arr) {
if (arr.length === 0) return undefined;
return [
min(arr),
max(arr),
avg(arr)
];
}
In case you want to see all the code for this exercise you should check this commit on GitHub.com.
In Conclusion
Functions are one of the pillar of programming. Virtually any more advanced concept is built on top of functions, that’s why you need to know them well. With this JavaScript tutorial, we gave you all the tools you need to understand them and work with them.
Yet, remembers that mastery comes from practice. Try practicing with function as much as you can, and continue following our Full Stack Development course to get practice on real-world use-cases.