Everything about Functions in JavaScript
Learn about functions in JavaScript along with concepts like pure functions, higher-order functions, and closures
Functions solve one of the most fundamental problems that humans have, “automating repetitive work”. Understanding functions ensure a happy married life with JavaScript.
So let’s start with creating a function. There are 2 ways to do so:
- Named function declaration
- Function expression
Named Function Declaration
To define a function you need the function
keyword along with the following things:
name
- a name for your function. A pro-tip - be concise and descriptiveparameters
- input variables for your functionbody
- the logic/functionality of your function with a return value
// name parameter
// 👇🏽 👇🏽
function getFood(food, quantity) {
const yourFood = food.repeat(quantity); // 👈🏽 logic
return yourFood; // 👈🏽 return value
}
If a return
value isn’t given it will return undefined
.
Calling and Referencing
You can execute a function by calling it using parentheses ()
and passing the required arguments.
getFood("🥪", 3); // 👈🏽 calling
getFood; // 👈🏽 reference
You can use the reference of a function stored in a variable and use it later and other Higher Order Function things.
Parameters and Arguments
- Parameters are variable names that you use in the function definition
- Arguments are the values/expression that are the actual values passed as input in the function call
Function Expression
A function expression is another way of creating a function where an anonymous function is created and the value is assigned to a variable.
// variable
// 👇🏽
const getFood = (food, quantity) => {
const yourFood = food.repeat(quantity);
return yourFood;
};
console.log(getFood("🥪", 3));
Here getFood
variable is holding an anonymous function.
Name Function Declaration vs Function Expression
Functions created using function declaration are hoisted whereas function expressions are not. Also, this
keyword behaves differently in both cases about which we will learn more in the Arrow Functions
section.
Which 🏆 one to use? There isn’t one best but function expressions are more recommended because of:
- It can be reassigned
- It can be passed around (Higher Order Functions)
- Since it's not hoisted, your code will be much easier to understand and debug
This is great, but what’s the modern way of writing functions in JavaScript? ↖️ Arrow Functions.
Arrow Functions
The concise and “elegant” way of writing functions in modern JavaScript. Converting regular function to an arrow function:
// Regular function
function getFood(food, quantity) {
return food.repeat(quantity);
}
// Arrow function
const getFood = (food, quantity) => {
return food.repeat(quantity);
};
// Directly returning
const getFood = (food, quantity) => food.repeat(quantity);
💡 For returning something directly using the arrow function, wrap it with parentheses ()
.
const breakfast = () => ({
food: "🥪",
quantity: 3,
});
This conciseness is what makes arrow functions so 💪🏽 powerful.
But it’s more than just syntactic sugar for writing functions. There are some nuances that make it different from a regular function like:
- It will implicitly return the value when you omit curly braces
{}
. For example:() => true
- Its always an expression and thus it can’t be hoisted
- 🔥 It doesn’t have its own
this
context as regular functions do
The last point has some big implications. To know more about it and arrow functions read my article on How Arrow Functions redefine this in JavaScript.
What’s better, to be pure and impure? Find out in the next section, 👼 Pure Functions.
Pure Functions
A pure function is a function that:
- relies only on its input,
- produces no side-effects and
- does not modify variables outside its scope
These conditions make pure functions give the same output for the same input. Functions that don’t adhere to these conditions are termed impure functions.
🤔 What a side-effect? Operations of a function that uses OR modifies non-local states. This makes the function indeterministic. console.log
is a side-effect because it prints the output in the console i.e. outside the function.
An example of an impure function:
let x = 0;
const impure = (x) => {
x = x + 1; // side-effect
console.log(x); // side-effect
return x;
};
An example of a pure function:
const pure = (x) => x * 2;
In the above example we get same output from the same input and checks all the conditions for a pure function.
The next topic is what you will be using the most while working with functions in JavaScript i.e. Higher Order Function.
Higher Order Function
In JavaScript, functions are first-class citizens. This means that:
- they can be 🔄 passed around and used as arguments for other functions
- they can be returned from other functions
- they can be assigned to variables
In the following example, we have used functions like first-class citizens. The callback
parameter in the getFood
function is a type of code commonly used in JavaScript.
// Assigning a function to a variable
const milkshakeIngredients = () => ({ mango: "🥭", milk: "🥛" });
const getFood = (callback) => {
const ingredients = callback();
console.log(ingredients);
return () => ({ status: "Mango milkshake is ready" }); // returning a function
};
// Passing function as an argument
const result = getFood(milkshakeIngredients);
console.log(result()); // calling the returned function's reference
How to nail down JavaScript interview? By understanding 🍷 Closures.
Closures
Closures are functions that have access to variables from an outer scope. Whenever you’re defining a function, you’re creating a lexical environment.
- The inner function will have access to the things defined in outer scopes
- but the outer objects won’t have access to the inner function’s lexical environment
Consider the following code example to understand how scopes work:
const fish = "🐟";
const outer = () => {
const crocodile = "🐊";
// Can access `fish` (from its outer scope) and `crocodile` from its own scope
console.log(fish);
console.log(crocodile);
// ReferenceError: whale is not defined
// Cannot access whale from `inner` function's scope
try { console.log(whale); }
catch { console.log("cannot access 🐳"); }
const inner = () => {
const whale = "🐳";
// Can access everything from its own and outer scopes
console.log(fish);
console.log(crocodile);
console.log(whale);
};
inner();
};
outer();
// Output:
// 🐟
// 🐊
// cannot access 🐳
// 🐟
// 🐊
// 🐳
The inner
function can access variables from the outer
function and the global
scope. Whereas the outer
function can access value only from the global
scope because it's inside that scope but cannot access variables from the inner
function's scope.
Now consider the following example of a closure:
const chooseFruit = () => {
let fruit = "🍎";
const favFruit = () => {
console.log(fruit);
};
return favFruit;
};
const favouriteFruit = chooseFruit();
favouriteFruit(); // 🍎
In this example, favouriteFruit
is a reference of favFruit
as chooseFruit
returns favFruit
function. When you run favouriteFruit
it logs 🍎
as output, which it has access to the fruit
variable. This is why favouriteFruit
is a closure.
Conclusion
Now you know about:
- 🐥 How to create functions
- ➡️ Arrow functions
- 👨👩👧 High order functions
- 👼 Pure functions
- ⚾️ Closures
If you liked this post then do 👍🏼 like and give your 🎙 feedback. ✅ Follow to stay up to date with my new posts.