Functions can be thought of as one of the core building blocks of our JavaScript programs. A function is simply a set of statements designed to perform a particular task ā which is executed whenever it is invoked (or ācalledā).
In this article weāre going to take a look at defining functions, function expressions, calling functions, scope, nesting, closures, the arguments object, function parameters & arrow functions!
Defining functions
A function definition (otherwise known as a function declaration, or function statement) will always begin with theĀ function
keyword. What follows is theĀ nameĀ of our function, anyĀ parametersĀ enclosed in parenthesis, and then our JavaScriptĀ statementsĀ (enclosed inĀ { }
) which are executed when our function is called.
Letās see an example:
function multiply(a, b) {
return a * b;
}
Our functionĀ multiply
Ā takes two parameters (a
Ā andĀ b
). Included in-between the curly braces is a statement that returns the result of the first parameterĀ a
Ā multiplied by the second parameterĀ b
.
Function expressions
There is another way to define a function, known as the function expression. These types of functions can actually beĀ anonymous. They donāt need to be given a name.
For example, our previousĀ multiply
Ā function couldāve also been defined like so:
let multiply = function(a, b) { return a * b; };
let x = multiply(2,2); // x returns 4
A typical use case for function expressions might be when passing a function as an argument to another function.
WeĀ canĀ also define a function based on a condition. For example, the following function definesĀ addItem
Ā only ifĀ num
Ā equals 1:
let addItem;
if (num === 1) {
addItem = function(shoppingList) {
shoppingList.item = 'Apples';
}
}
Calling functions
When we define a function all weāve done is given it a name & specified what to do when the function executes. So to actually run the function, weāll need to call it within our program!
To call our earlier functionĀ multiply
, we could invoke it as follows:
multiply(2,2);
Here weāre calling our function which is set to receive two arguments with the arguments ofĀ 2
Ā andĀ 2
. The function executes and runs its statements which returns the result ofĀ 4
(2 multiplied by 2).
Functions need to be in scope when they are called, but the function declaration can be hoisted (appear below the call in the code), like so:
console.log(multiply(2,2));
/* ... */
function multiply(a,b) { return a * b; }
The scope of a function is the function in which it is declared, or the entire program if it is declared at the global level.
Note:Ā This only works with function declarations. Not function expressions!
Function scope
When variables are defined inside a function they cannot be accessed anywhere outside of that function. As that would be outside the scope. However, a function is able to access all variables and functions defined inside the scope in which it is defined. So a function defined in the global scope can access all variables defined in the global scope. A function defined inside another function can also access all variables defined in its parent function and any other variable to which the parent function has access.
Letās see an example...
// These variables are defined globally (global scope)
let a = 10,
b = 3,
name = 'Bruce';
// This function is defined globally
function multiply() {
return a * b;
}
multiply(); // returns 30
// Working with a nested function
function multiplyAssign() {
let a = 20,
b = 6;
function add() {
return name + ā receivedā + (a * b);
}
return add();
}
multiplyAssign(); // returns "Bruce received 120"
Nesting functions and closures
So you can nest a function within a function!Ā The nested (inner) function is private to its containing (outer) function. It also forms aĀ closure.Ā A closure is an expression (usually a function) that can have free variables together with an environment that binds those variables (it āclosesā the expression).
And as a nested function is a closure, itās able to āinheritā the arguments and variables of its containing function.
As follows:
function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2, 3); // returns 13
b = addSquares(3, 4); // returns 25
c = addSquares(4, 5); // returns 41
Since the inner function forms a closure, you can call the outer function and specify arguments for both the outerĀ andĀ inner function!
Closures
As we now know, we can nest our functions and JavaScript will give the inner function full access to all the variables and functions defined inside the outer function (as well as all variables & functions that it has access to).
However, the outer function does not have access to the variables and functions defined inside the inner function!Ā AĀ closureĀ is created when the inner function is somehow made available to any scope outside the outer function.
Letās see an example:
// The outer function defines a "name" variable
let user = function(name) {
let getName = function() {
return name;
// The inner function can access the "name" variable of the outer function
}
// Return the inner function, and expose it to outer scopes
return getName;
}
makeAdmin = user('Steve Stevesson');
makeAdmin(); // returns "Steve Stevesson"
The arguments object
The arguments of any function are maintained in anĀ array-likeĀ object. Within a function, you can address the arguments passed to it as follows:
arguments[i]
HereĀ i
Ā is the first number of the argument, starting at zero. So, the first argument passed to a function would beĀ arguments[0]
. The total number of arguments would beĀ arguments.length
.
Using theĀ arguments
Ā object, you can call a function with more arguments than it is formally declared to accept. This is often useful if you don't know in advance how many arguments will be passed to the function.Ā You can useĀ arguments.length
Ā to determine the number of arguments actually passed to the function, and then access each argument using theĀ arguments
Ā object.
For example, consider a function that concatenates several strings. The only formal argument for the function is a string that specifies the characters that separate the items to concatenate. The function is defined as follows:
function myConcat(separator) {
let result = ''; // initialize list
let i;
// loop through arguments
for (i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}
You can pass any number of arguments into this function, and itāll concatenate each argument into a string ālistā:
myConcat(', ', 'fred', 'wilma', 'barney', 'betty');
// returns "fred, wilma, barney, betty, "
myConcat('; ', 'marge', 'homer', 'lisa', 'bart', 'maggie');
// returns "marge; homer; lisa; bart; maggie; "
myConcat('. ', 'jerry', 'kramer', 'elaine', 'george', 'newman');
// returns "jerry. kramer. elaine. george. newman. "
Note:Ā TheĀ arguments
Ā variable is "array-like", but not an array. It has a numbered index and aĀ length
Ā property. However, it does not possess all of the array-manipulation methods.
Function parameters
There are two kinds of function parameters:Ā defaultĀ parameters andĀ restĀ parameters. Letās now take a look at each...
Default parameters
In JavaScript, parameters of functions will default toĀ undefined
. However, in some situations, it might be useful to set a different default value. This is where default parameters are useful.
Itās actually quite simple to implement:
function multiply(a, b = 1) {
return a * b;
}
multiply(10); // 10
You can simply putĀ 1
Ā as the default value forĀ b
Ā in the function head.
Rest parameters
The rest parameter syntax allows us to represent an indefinite number of arguments as an array.
In this example, we use the rest parameters to collect arguments from the second one to the end. We then multiply them by the first one. This example uses an arrow function, which weāll look at next!
function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}
let arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]
Arrow functions
An arrow function has aĀ muchĀ shorter syntax compared to function expressions. For example, here is a regular function:
function funcName(params) {
return params + 10;
}
funcName(2); // 12
And here is the same function, expressed as anĀ arrow function:
let funcName = (params) => params + 10
funcName(2); // 12
The same function in just one line of code! Pretty neat!
If we have no parameters, we express our arrow function like this:
() => { statements }
And when we have just one parameter, the opening parenthesis are optional:
parameters => { statements }
Finally, if youāre returning an expression, you can remove the brackets:
parameters => expression
// is the same as:
function (parameters){
return expression;
}
Note:Ā Unlike a regular function, an arrow function does not bindĀ this
. Instead, this
Ā is bound to keep its meaning from its original context. Weāll be taking a closer look at the āthisā keyword in an upcoming article!
Predefined functions
Itās well worth noting that JavaScript has numerous built-in functions! And theyāll likely save you a lot of time, especially for common tasks. See:
https://www.tutorialspoint.com/javascript/javascript_builtin_functions.htm
Wrapping up
And thatās all for today! Weāve learned about defining functions, function expressions,Ā callingĀ functions, scope, nesting, closures, the arguments object, function parameters & arrow functions!
Related Posts:
A little about me..
Hey, Iām Tim! š
Iām a freelance business owner, web developer & author. I teach both new and experienced freelancers how to build a sustainable and successful freelancing business. Check out myĀ Complete Guide to Freelancing if you'd like to find out more.
While you're here, you can browse through my blogs where I post freelancing tips, code tutorials, design inspiration, useful tools & resources, and much more! You can also join the newsletter, or find me on X.
Thanks for reading! š