The Basics: JavaScript Functions
Understanding how to work with functions is essential for any JavaScript developer. This post goes over the basics of functions with easy-to-understand examples to get you comfortable with the concept!
Introduction
Functions are one of the fundamental building blocks in JavaScript. A function in JavaScript is a block of code that performs a specific task and can be reused multiple times. They are an essential part of the language and are used to group together related code, create reusable code snippets, and structure larger programs. To use a function, it must be defined somewhere within the scope from which you wish to call it.
Function Declaration
A function definition (declaration/statement) consists of the function keyword followed by:
- The name of the function
- A list of zero or more parameters to the function, enclosed in parentheses () and separated by commas.
- The code to be executed by the function enclosed in curly brackets {}.
Consider the following function named square. This function takes one parameter called number, and consists of one statement that returns the parameter multiplied by itself.
function square(number) {
return number * number;
}
console.log(square(2)); // Result: 4
If the code within the function assigns a new value to the parameter, the change is not reflected globally or in the code that called the function. However, when you pass an object as a parameter, if the function changes the object's properties, that change is visible outside the function.
function incrementAge(person) {
person.age += 1;
}
const johnDoe = { name: 'John Doe', age: 24 };
incrementAge(johnDoe);
console.log(johnDoe.age); // Result: 25
Similarly, if an array is passed as a parameter, any changes to the array's values will be visible outside the function. This is because arrays are passed by reference in JavaScript, meaning that the function receives a reference to the original array, rather than a copy of it.
function doubleArray(arr) {
for (let i = 0; i < arr.length; i++) {
arr[i] = arr[i] * 2;
}
}
const numbers = [1, 2, 3, 4];
doubleArray(numbers);
console.log(numbers); // Result: [2, 4, 6, 8]
Function Expressions
A function expression is very similar to a function declaration - with a main difference being that the function name can be omitted in function expressions to create anonymous functions. We can create a function expression using the function constructor and a function declaration. Function expressions are convenient when passing a function as an argument to another function - such as a callback.
const getArea = function (width, height) {
return width * height;
}
console.log(getArea(3, 4)); // Result: 12
Arrow Functions
Arrow functions (often referred to with fat arrows to distinguish from the normal -> arrow) are another - more concise - way to write function expressions. Arrow functions do not have this, arguments, super, or new.target and are always anonymous. As an arrow function does not have its own this, the this value of the enclosing context is used.
const square = (number) => {
return number * number;
}
As this function only has a single line of code, we can omit the curly brackets and the return keyword. Furthermore, since there is only a single parameter, we can also remove the parentheses around it.
const square = number => number * number;
If we want to return an object literal expression, we simply need to wrap the function body in parenthesis (in addition to curly brackets):
const example = () => ({ city: 'Toronto' });
console.log(example().city); // Result: Toronto
Function Hoisting
Consider the following example:
console.log(square(5)); // Result: 25
function square(number) {
return number * number;
}
This code runs without error, despite the square() function being called before it is declared. This is because the JavaScript interpreter hoists the entire function declaration to the top of the current scope, so the above code is equivalent to:
function square(number) {
return number * number;
}
console.log(square(5)); // Result: 25
Note that function hoisting only works with function declarations - not with function expressions or arrow functions.
Function Scope
Variables that are defined within a function are only accessible within the function and are not visible outside of it. This is because the variable is only defined within the scope of the function. On the other hand, a function can access all variables and functions that are defined within the same scope.
In other words, a function defined in the global scope can access all variables defined in the global scope. Similarly, a function defined within another function can access all variables defined in the parent function, as well as any other variables that the parent function has access to.
Nested Functions
It is possible to define a function inside of another function. The inner function is only accessible within the outer function and is considered private to it.
The inner function also forms a closure. A closure is an expression (usually a function) that has access to variables outside of its own scope, in addition to its own variables.
Since a nested function is a closure, it can access the variables and arguments of the outer function. In other words, the inner function has access to the scope of the outer function.
function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
const insideFn = outside(3); // This stores a function that adds 3 to whatever you give it.
const resultOne = insideFn(5); // Result: 8
const resultTwo = outside(3)(5); // Result: 8
Default & Rest Parameters
In JavaScript, function parameters default to undefined if no value is provided. However, in some situations it might be useful to set a different default value. This is exactly what default parameters do - let's take a look at a simple example.
function multiply(a, b = 1) {
return a * b;
}
multiply(5); // 5
We can see that since no b value was provided, the function used the default of 1 to return a result rather than simply break.
The rest parameter syntax allows us to represent an indefinite number of arguments as an array. In the following example, the function multiply uses rest parameters to collect all arguments excluding the first. The function then multiplies these by the first argument.
function multiply(multiplier, ...args) {
return args.map(x => multiplier * x);
}
const arr = multiply(2, 1, 2, 3); // [2, 4, 6]
Conclusion
In conclusion, functions are an important building block in JavaScript, allowing developers to group together related code and reuse it multiple times. We discussed various ways to create functions, what function hoisting is, the scope of a function, nested functions, and function parameters. I hope that this post has been helpful, as understanding how to work with functions is essential for any JavaScript developer.