TypeOfNaN

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!

If you'd like to support this blog by buying me a coffee I'd really appreciate it!

Nick Scialli

Nick Scialli is a senior UI engineer at Microsoft.

© 2024 Nick Scialli