Functions
We separate tiny portions of our code into a function so we can call this function multiple times, move it to a separate file, and much more.
In any moderately complex JavaScript program, everything happens inside functions.
Functions are a core, essential part of JavaScript.
What is a function? A function is a block of code, self contained, and we can tell our program to ran that code when we need it.
Here’s a function declaration:
The // do something
comment is a placeholder for a set of instructions that can be long as much as you want.
When you want to run this function, you “call it” or “invoke it”, like this:
Function parameters
A function can have no parameter, like we’ve seen previously:
Or it can have one or more parameters, which are declared inside the parentheses:
When we can pass a parameter, we invoke the function passing arguments:
The difference between arguments and parameters is this: you define the parameters and that’s what you see inside the function. Arguments are passed by the program when you call the function.
They are basically the value assigned to the parameter.
Functions can have default values for the parameters that are not passed by the caller:
Returning values from a function
A function can have a return value.
By default a function returns undefined
.
If you want to return a value from it, you add a return
keyword with a value:
In your program you can assign the return value of the function to a variable, when you invoke the function:
result
now holds a string with the hi!
value.
One thing to note is that you can “return early” from a function, and this is very useful for example when you need to meet a condition before going on with your program:
You can only return one value from a function.
A “trick” to return multiple values from a function is to return an object, or an array, like this:
Then you can call the function and save your array to a variable, or use array destructuring like this:
Arrow functions
Arrow functions are very often used instead of “regular” functions, the one I described in the previous chapter. You’ll find both forms used everywhere.
Visually, they allows you to write functions with a shorter syntax, from:
to
But.. notice that we don’t have a name here.
Arrow functions are anonymous. We must assign them to a variable.
We can assign a regular function to a variable, like this:
When we do so, we can remove the name from the function:
and invoke the function using the variable name:
Although it’s generally recommended to avoid so, for one simple reason: when you have errors, naming your functions helps JavaScript give you back helpful error messages, so you can fix your bugs quicker.
That’s the same thing we do with arrow functions:
If the function body contains just a single statement, you can omit the parentheses and write all on a single line:
Parameters are passed in the parentheses:
If you have one (and just one) parameter, you could omit the parentheses completely:
Arrow functions allow you to have an implicit return: values are returned without having to use the return
keyword.
It works when there is a on-line statement in the function body:
Like with regular functions, we can have default parameters, in case they are not passed:
And as with regular functions, we can only return one value.
The are very similar to regular functions. The big difference with regular functions is when they are used as object methods and their relation to the this
keyword, which is something we’ll soon look into.
Nesting functions
Functions can be defined inside other functions:
Arrow functions can contain other arrow function, or also regular functions. You can mix them:
The nested function cannot be called from the outside of the enclosing function. Just from inside it.
Immediately-invoked functions
An immediately-invoked function expression (we’ll call them IIFE) is a way to execute functions immediately, as soon as they are created, without waiting for another line of code to call them.
This is the syntax that defines an IIFE:
We used a pair of parentheses to enclose the function, then we append ()
to call it.
IIFEs can be defined with arrow functions as well, in the same way:
We basically have a function defined inside parentheses, and then we append ()
to execute that function: (THE FUNCTION)()
.
Those wrapping parentheses are actually what make our function, internally, be considered an expression. Otherwise, the function declaration would be invalid, because we didn’t specify any name.
Remember that function declarations want a name, while function expressions do not require it.
You could also put the invoking parentheses inside the expression parentheses, there is no difference, just a styling preference:
IIFEs are one of the rare cases when you need a semicolon in the previous line.
This is because of the strange syntax, JavaScript tries to concatenate the lines, and as it sees a parentheses opening, it thinks the previous line was a function name and now we add a parameter.
To fix this problem you might see this:
This completely prevents the issue, so it’s a good practice when you write an IIFE.
Why do we need them sometimes?
We have 2 cases.
One is isolation from the outside.
Functions create a new scope, so any variable defined inside an IIFE is not going to “leak” outside. This is something very common when you have multiple pieces of JavaScript running in a page, that should not interact with each other. The best example of this is a WordPress site that has a dozen plugins all adding their own JavaScript.
If by chance two define a variable or a function with the same name, we have compatibility issues. IIFEs should help you isolate this kind of problem.
You can use this technique in your own code to isolate variables within a IIFE.
Another use case is when you have code that runs inside a loop, and you must do something asynchronously. We haven’t talked about asynchronous code yet, so we’ll leave this explanation for another lesson.
Recursive functions
A function has the ability to call itself.
This is what we call recursion.
Recursion allows us to solve some specific problems in a smart way.
You need a function with a name, which can be either a “regular” function, or an arrow function:
Now you can call test()
inside test()
.
The simplest example we can make is calculating a factorial of a number.
The factorial of a number is what we get by multiplying the number for (number - 1), (number - 2), and so on until we reach the number 1.
The factorial of 3 is
(3 * (3 - 1) * (3 - 2)) = 3 * 2 * 1
which is 6
The factorial of 4 is
(4 * (4 - 1) * (4 - 2) * (4 - 3)) = 4 * 3 * 2 * 1
which is 24.
We can create a recursive function to calculate the factorial automatically:
Now we can call this function passing the number for which we want to calculate the factorial.
We can also use an arrow function if we prefer, there’s no difference:
With recursive functions you have to pay attention to not generating an error by calling a function an infinite amount of times.
What do I mean? Imagine we do an error, and instead of calculating the factorial as
we do this:
As you can see, instead of decreasing n
each time we call factorial()
, we are now calling factorial(n)
ad infinitum. There’s no end, because we forgot to lower it on every call.
If you run this code, you’ll get this error:
Every time a function is invoked, JavaScript needs to remember the current context before switching to the new one, so it puts that context on the call stack. As soon as the function returns, JavaScript goes to the call stack and picks the last element that was added, and resumes its execution.
Maximum call stack size exceeded means that too many elements were put on the stack, and your program crashed.
It’s similar to having an infinite loop, because the program can’t continue its execution.
Last updated
Was this helpful?