const - JS | lectureA Closer Look At Functions

Functions Accepting Callback Functions

A Closer Look At Functions

Now that we know what higher-order functions are let's create our own to demonstrate how they work. So in this lecture, we're going to create a function that accepts other functions as input. But to start, let's write two very generic functions that do simple string transformations.

script.js
const replaceSpaces = function (str) {
  return str.replace(/\s/g, '').toLowerCase();
};

const capitalizeFirstWord = function (str) {
  const [first, ...rest] = str.split(' ');
  return [first.toUpperCase(), ...rest].join(' ');
};

Let's now create our higher-order function that will accept our two functions as input.

script.js
// Higher-order function
const transform = function (str, fn) {
  // Call the function with the string as input
};

transform('Hello world!', capitalizeFirstWord);

By calling the transform function as we did above, we want it to transform the 'Hello world!' string using the capitalizeFirstWord function. Notice how we only pass the function value when calling the transform function. We are not calling it. It is the transform function that is responsible for calling the provided function.

Let's complete the transform function.

script.js
const transform = function (str, fn) {
  console.log(`Original string: ${str}`);

  /**
   * Inside this function, `capitalizeFirstWord` is now `fn`
   */
  console.log(`Transformed string: ${fn(str)}`);

  /**
   * In the last lecture we saw that functions have methods. And besides
   * methods, functions can also have properties. One of them is the `name` property.
   */

  console.log(`Transformed by: ${fn.name}`);
};

Callback Call - Caîtalize

The original string looks familiar from the above output, but then the transformed string indeed was transformed just as we hoped it would. The first word is now in uppercase. And that is, of course, the work of the capitalizeFirstWord function.

Finally, we can also see that it was transformed by capitalizeFirstWord. And so that's fn.name. So as the property says, it is just the function's name that can be used on any function in JavaScript.

Let's now try the samething with the replaceSpaces function.

script.js
transform('Hello world!', replaceSpaces);

Callback Call - Replace

Let's recap. When calling the transform function, we are passing a callback function. We call it a callback functions because we do not call it ourselves. But instead, we tell JavaScript to call it later.

And in this case, calling it later happens right here:

script.js
const transform = function (str, fn) {
  //...

  console.log(`Transformed string: ${fn(str)}`);
  //...
};

So it's the transform function that will call the callback function. And the callback function in the transform function is called fn. And so, that's the function's name that we have to call.

This is exactly the same idea that we already talked about, using the addEventListener function. Let's say we had this very simple greet function, which doesn't really do much except off logging something to the console.

script.js
const greet = function () {
  console.log('Hi there!');
}

Now let's say we have this:

script.js
/**
 * The `greet` callback function will be called by JavaScript
 * as soon as we click on the body.
 */
document.body.addEventListener('click', greet);

Don't worry about the document.body part. We are going to learn what it means later. What matters here is the addEventListener function. Just like our transform function, we are passing a callback function. And the callback function in an addEventListener function is also called the event handler or event listener. But that doesn't really matter. What matters is that conceptually, the greet function is the callback function, and the addEventListener function is the higher-order function.

// Higher-order function
addEventListener === transform

// Callback function
greet === capitalizeFirstWord or replaceSpaces

There are many other examples in the JavaScript language. And this concept of callback functions is used all the time in built in JavaScript functions.For example the forEach function that we call on arrays. Let's create an array here.

script.js
const arr = [1, 2, 3, 4, 5];
arr.forEach(greet);

Callback Call - ForEach

You can see from the above output that the greet function was called five times. That's because we have five elements in our array. And so, as the method's name says, this callback will be called for each of them. We will learn about the forEach method later. What matters here is that, once again, we used the concept of the callback function. And this is something really common in JavaScript.

Let's now take a minute or two to understand why callback functions are so much used in JavaScript and why they are so helpful? The first big advantage is that it makes it easy to split up or code into more reusable and interconnected parts.

script.js
/**
 * Each functionnality nicely split up into their own functions.
 */
const replaceSpaces = function (str) {/**/};

const capitalizeFirstWord = function (str) {/**/};

The second and most important advantage is that callback functions allow us to create abstraction. Abstraction means that we hide the details of some code implementation because we don't care about all that detail. And this allows us to think about problems at a more abstract level. And so that's why it's called an abstraction.

Considering the example we had at the beginning of this lecture, the transform function does not care how the string is transformed. It doesn't care about this level of detail. All it wants to do is transform a string, but it doesn't care how it should do it.

What I mean is that we could have taken the code we wrote in the replaceSpaces function and written it directly into transform, or even the code we wrote in capitalizeFirstWord. That would have worked just the same. But instead, we abstracted this code away into other functions. So again, we created a new level of abstraction, and by doing this, our main transform function is only concerned with transforming the input string itself. But no matter how that transforming itself actually works. So it's basically delegating the string transformation to the other lower level of functions, which are: replaceSpaces and capitalizeFirstWord.

Now, with this idea of abstraction, higher and lower levels of abstraction, it makes sense that our transform function is called a higher-order function. And again, that's basically because the transform function operates at a higher level of abstraction, leaving the low-level details to the low-level functions (replaceSpaces and capitalizeFirstWord).

Understanding this is crucial for your process. And I consider this one of the most important lectures here in the course because callback functions are a vital part of the JavaScript language. And that's one of the main takeaways from this lecture.

To finish, please take a minute to really review this lecture and build an example of your own maybe. And then I'll see you in the next lecture, where we will do the opposite of this one. i.e., having functions return other functions.