First-Class Functions in JavaScript
Nick Scialli
August 01, 2019
Note: Due to this post’s popularity, I decided to make a video version of it! Please check it out on YouTube here and subscribe for more videos.
Introduction
JavaScript has first-class functions. This means functions can be treated like other values, which turns out to be incredibly important behavior to understand as you advance as a JavaScript developer. The following three bullets represent important first-class function behavior:
- Functions can be assigned to variables
- Functions can be passed as arguments to other functions
- Functions can be returned from other functions
Let’s explore each of these bullets using basic examples and then bring it all together at the end with a couple more complex, real-world examples!
Assigning Functions to Variables
Let’s create a function that returns the text "Hello"
and then assign that function to a variable creatively named sayHello
.
const sayHello = () => {
return 'Hello';
};
console.log(sayHello());
// "Hello"
Pretty basic! Let’s move on.
Passing a Function as an Argument
Let’s take the aforementioned sayHello
function and pass it as an argument to another function. This other function will say hello to a particular person.
const sayHelloToPerson = (greeter, person) => {
return greeter() + ' ' + person;
};
console.log(sayHelloToPerson(sayHello, 'John'));
// Hello John
A quick note on what happens when we pass the sayHello
function to sayHelloToPerson
: within the sayHelloToPerson
function, the variable greeter
will now point to the sayHello
function in memory. When we call greeter()
, the function is called!
Note: I’d normally use template literals in this case (i.e., return `${greeter()} ${person}`
), but didn’t want template literal syntax to muddy the water in this example!
Return a Function from Another Function
Maybe we don’t alway want to say "Hello"
, but rather want the choice of creating any number of different types of greeters. Let’s use a function that lets us create greeter functions.
const greeterMaker = (greeting) => {
return (person) => {
return greeting + ' ' + person;
};
};
const sayHelloToPerson = greeterMaker('Hello');
const sayHowdyToPerson = greeterMaker('Howdy');
console.log(sayHelloToPerson('Joanne'));
// "Hello Joanne"
console.log(sayHowdyToPerson('Joanne'));
// "Howdy Joanne"
Our greeterMaker
is a function that creates functions! Pretty nifty, right?
Some Fun, Real-World Use Cases
Now that we checked out the basics of first-class functions, let’s look at some real-world use cases!
Object Validation
Perhaps you have a bunch of criteria that an object (e.g., new user information) needs to pass to be considered “valid.” Let’s create a function that iterates over all our criteria and returns whether the object is valid or not.
const usernameLongEnough = (obj) => {
return obj.username.length >= 5;
};
const passwordsMatch = (obj) => {
return obj.password === obj.confirmPassword;
};
const objectIsValid = (obj, ...funcs) => {
for (let i = 0; i < funcs.length; i++) {
if (funcs[i](obj) === false) {
return false;
}
}
return true;
};
const obj1 = {
username: 'abc123',
password: 'foobar',
confirmPassword: 'foobar',
};
const obj1Valid = objectIsValid(obj1, usernameLongEnough, passwordsMatch);
console.log(obj1Valid);
// true
const obj2 = {
username: 'joe555',
password: 'foobar',
confirmPassword: 'oops',
};
const obj2Valid = objectIsValid(obj2, usernameLongEnough, passwordsMatch);
console.log(obj2Valid);
// false
If you’re relatively new to JavaScript, it might take you several read-throughs to understand what’s going on, but trust me, the payoff is great once you understand!
Note: If you’re unfamiliar with the rest operator (...
), this simply allows all remaining arguments provided to the function to be put into an array by the specified name.
API Key Closure
Perhaps we have a situation where we want to connect to an API using an API key. We could add this key on every request, or, we could create a function that takes an API key parameter and returns functions that contain the API key with each request.
Important: Don’t put secret API keys in front-end code. Instead, imagine the following code is in a node-based back-end.
const apiConnect = (apiKey) => {
const getData = (route) => {
return axios.get(`${route}?key=${apiKey}`);
};
const postData = (route, params) => {
return axios.post(route, {
body: JSON.stringify(params),
headers: {
Authorization: `Bearer ${apiKey}`,
},
});
};
return { getData, postData };
};
const api = apiConnect('my-secret-key');
// No need to include the apiKey anymore
api.getData('http://www.example.com/get-endpoint');
api.postData('http://www.example.com/post-endpoint', { name: 'Joe' });
Conclusion
And there you have it! Hopefully by now you see that functions in JavaScript are truly first-class and how important functions can be to your JavaScript development career. I recommend practicing all sorts of ways to use functions in your code!
Nick Scialli is a senior UI engineer at Microsoft.