Callbacks

VIDEO

Callback Functions

Recall that JavaScript functions are first-class functions. We can do with functions just about everything we can do with other values -- including passing them into _other _functions! A function that takes other functions as arguments (and/or _returns _a function, as we learned in the previous section) is known as a higher-order function. A function that is passed as an argument into another function is called a callback function.

We'll be focusing on callbacks in this section. Callback functions are great because they can delegate calling functions to other functions. They allow you to build your applications with composition, leading to cleaner and more efficient code.

Let's jump right in!

VIDEO

Here's the code from the preceding video.

QUESTION 1 OF 5

What is true about callbacks? Select all that apply:

  • A function that is passed as an argument to another function is called a callback.

  • A function that takes another function in as an argument is a higher-order function.

  • Leveraging callbacks is possible because JavaScript functions are_first-class_functions.

  • Callback functions do not work in the browser.

SUBMIT: Callback functions aren't exclusive to JavaScript, but they're incredibly common and crucial for many of the patterns you'll read and write!

QUESTION 2 OF 5

Consider the following two functions:

function each(array, callback) {
  for (let i = 0; i < array.length; i++) {
    if (callback(array[i])) {
      console.log(array[i]);
    }
  }
}
function isPositive(n) {
  return n > 0;
};

The following is then executed:

each([-2, 7, 11, -4, -10], isPositive);

What is outputted to the console?

  • -2,-4,-10

  • -7,-11

  • -2,7,11,-4,-10

  • 7,11

SUBMIT: There's quite a bit going on, so let's break it down each step!

Theeach()function takes in two arguments: anarray, andcallbackfunction. The code within comprises of aforloop and a conditional: it first iterates through all the values of a suppliedarrayargument, then prints out that values only _if _itscallbackfunction returnstrue.

TheisPositive()function returns a boolean depending on the argument passed in (i.e.,trueif the number passed in is positive, andfalseif not).

As such, wheneach([-2, 7, 11, -4, -10], isPositive);is executed, theeach()function iterates through the entire array and only prints out values to the console that returntruewhen tested against the callback function:7and11.

Array Methods

Where have you probably seen callback functions used? In array methods! Functions are commonly passed into array methods and called on elements _within _an array (i.e., the array on which the method was called).

Let's check out a couple in detail:

  • forEach()
  • map()
  • filter()

forEach()

Array'sforEach()method takes in a callback function and invokes that function _for each _element in the array. In other words,forEach()allows you to iterate (i.e., loop) through an array, similar to using aforloop. Check out its signature:

array.forEach(function callback(currentValue, index, array) {
    // function code here
});

The callback function itself receives the arguments: the current array element, its index, and the entire array itself.

Let's say we have a simple function,logIfOdd(), that takes in a single number and logs it to the console if that number is an odd number:

function logIfOdd(n) {
  if (n % 2 !== 0) {
    console.log(n);
  }
}

logIfOdd(2);
// (nothing is logged)

logIfOdd(3);
// 3

When2is passed into the function,logIfOdd()does not output anything to the console because2is an even number. However, when3is passed into the function,3_is _logged to the console because it's an odd number.

ThelogIfOdd()function works great for individual numbers, but what if we want to check _an entire array _and log only the odd numbers within it?

[1, 5, 2, 4, 6, 3]

We can iterate through the above array withforEach()and simply pass it thelogIfOdd()function!

[1, 5, 2, 4, 6, 3].forEach(function logIfOdd(n) {
  if (n % 2 !== 0) {
    console.log(n);
  }
});

// 1
// 5
// 3

In the above array, only the numbers that are odd numbers are printed to the console. Let's recap what happened:logIfOdd()is a function and is passed in as an argument toforEach().forEach()then invokeslogIfOdd()for each element in the array (i.e.,[1, 5, 2, 4, 6, 3]), which outputs1,5, and3.

Keep in mind that it's quite common to pass an_anonymous function_as an argument inforEach()as well:

[1, 5, 2, 4, 6, 3].forEach(logIfOdd);

// 1
// 5
// 3

Alternatively, it's possible to simply pass in just the name of the function as well (i.e., assuming the function was already defined, of course).

[1, 5, 2, 4, 6, 3].forEach(logIfOdd);

// 1
// 5
// 3

The three different ways shown each produce the same output (i.e., logging1,5, and3to the console).

Now, let's seeforEach()in action!

VIDEO

Here's the code from the preceding video.

QUESTION 3 OF 5

Which of the following are valid ways to iterate through an array and log each value to the console?

  • [1, 2, 3, 4].forEach(function(num) { console.log(num);});

  • [1, 2, 3, 4].forEach(function() { console.log(num);});

  • function logNum (num) { console.log(num);} [1, 2, 3, 4].forEach(logNum);

  • [1, 2, 3, 4].forEach(console.log);

SUBMIT

map()

Array'smap()method is similar toforEach()in that it invokes a callback function for each element in an array. However,map()returns a_new array _based on what's returned from the callback function. Check out the following:

const names = ['David', 'Richard', 'Veronika'];

const nameLengths = names.map(function(name) {
  return name.length;
});

Let's go over what's happening here. Themap()method works on arrays, so we have to have an array to start with:

const names = ['David', 'Richard', 'Veronika'];

We callmap()on thenamesarray and pass it an anonymous function as an argument:

names.map(function(name) {
  return name.length;
});

The function that's passed tomap()gets called _for each item _in thenamesarray! The function receives the first name in the array, stores it in thenamevariable and returns its length. Then it does that again for the remaining two names.

Remember that the key difference betweenforEach()andmap()is thatforEach()doesn't return anything, whilemap()returns a new array with the values that are returned from the function:

const nameLengths = names.map(function(name) {
  return name.length;
});

SonameLengthswill be a _new _array:[5, 7, 8]. Again, it is important to understand that themap()method returns a new array; it does not modify the original array.

This was just a brief overview of how themap()method works. For a deeper dive, check outmap()on MDN.

/* Using map()
 *
 * Using the musicData array and map():
 *   - Return a string for each item in the array in the following format:
 *     <album-name> by <artist> sold <sales> copies
 *   - Store the returned data in a new albumSalesStrings variable
 *
 * Note:
 *   - Do not delete the musicData variable
 *   - Do not alter any of the musicData content
 *   - Do not format the sales number; leave it as a long string of digits
 */

const musicData = [
    { artist: 'Adele', name: '25', sales: 1731000 },
    { artist: 'Drake', name: 'Views', sales: 1608000 },
    { artist: 'Beyonce', name: 'Lemonade', sales: 1554000 },
    { artist: 'Chris Stapleton', name: 'Traveller', sales: 1085000 },
    { artist: 'Pentatonix', name: 'A Pentatonix Christmas', sales: 904000 },
    { artist: 'Original Broadway Cast Recording', 
      name: 'Hamilton: An American Musical', sales: 820000 },
    { artist: 'Twenty One Pilots', name: 'Blurryface', sales: 738000 },
    { artist: 'Prince', name: 'The Very Best of Prince', sales: 668000 },
    { artist: 'Rihanna', name: 'Anti', sales: 603000 },
    { artist: 'Justin Bieber', name: 'Purpose', sales: 554000 }
];

// const albumSalesStrings = 'Replace this message with your code!';

const albumSalesStrings = musicData.map(function(data) {
    return data.name + ' by ' + data.artist + ' sold ' + data.sales + ' copies';

console.log(albumSalesStrings);
  • RESET QUIZ
  • TEST RUN
  • SUBMIT ANSWER

filter()

Array'sfilter()method is similar to themap()method:

  • It is called on an array
  • It takes a function as an argument
  • It returns a new array

The difference is that the function passed tofilter()is used as a test, and only items in the array that pass the test are included in the new array. Consider the following example:

const names = ['David', 'Richard', 'Veronika'];

const shortNames = names.filter(function(name) {
  return name.length < 6;
});

Just as before, let's break it down a bit! We have the starting array:

const names = ['David', 'Richard', 'Veronika'];

We callfilter()on thenamesarray and pass it a function as an argument:

names.filter(function(name) {
  return name.length < 6;
});

Again, just like withmap(), the function that's passed tofilter()gets called _for each item _in thenamesarray. The first item (i.e.,'David') is stored in thenamevariable. Then the test is performed -- and this is what's doing the actual filtering. First, it checks the length of the name. If it's6or greater, then it's skipped (and _not _included in the new array!). But, if the length of the name is less than6, thenname.length < 6returnstrueand the name _is _included in the new array!

And lastly, just like withmap(), thefilter()method returns a_new_array instead of modifying the original array:

const shortNames = names.filter(function(name) {
  return name.length < 6;
});

console.log(shortNames);
// ['David']

Above, the value ofshortNamesis the new array:['David']. Notice that it only has one name in it, because both'Richard'and'Veronika'are6characters or longer, and were filtered out.

This was just a brief overview of how thefilter()method works. For a deeper dive, check outfilter()on MDN.

/* Using filter()
 *
 * Using the musicData array and filter():
 *   - Return only album objects where the album's name is
 *     10 characters long, 25 characters long, or anywhere in between
 *   - Store the returned data in a new `results` variable
 *
 * Note:
 *   - Do not delete the musicData variable
 *   - Do not alter any of the musicData content
 */

const musicData = [
    { artist: 'Adele', name: '25', sales: 1731000 },
    { artist: 'Drake', name: 'Views', sales: 1608000 },
    { artist: 'Beyonce', name: 'Lemonade', sales: 1554000 },
    { artist: 'Chris Stapleton', name: 'Traveller', sales: 1085000 },
    { artist: 'Pentatonix', name: 'A Pentatonix Christmas', sales: 904000 },
    { artist: 'Original Broadway Cast Recording', 
      name: 'Hamilton: An American Musical', sales: 820000 },
    { artist: 'Twenty One Pilots', name: 'Blurryface', sales: 738000 },
    { artist: 'Prince', name: 'The Very Best of Prince', sales: 668000 },
    { artist: 'Rihanna', name: 'Anti', sales: 603000 },
    { artist: 'Justin Bieber', name: 'Purpose', sales: 554000 }
];

// const results = 'Replace this message with your code!';

const results = musicData.filter(function(data) {
    return (data.name.length >= 10 && data.name.length <= 25);

console.log(results);
  • RESET QUIZ
  • TEST RUN
  • SUBMIT ANSWER

Summary

JavaScript functions can take in a variety of different arguments, including strings, numbers, arrays, and objects. Since functions are first-class functions, functions can _also _be passed as arguments to a given function. Functions that take in other functions as arguments are called higher-order functions. Functions that are passed as arguments to other functions are called callback functions.

Callbacks allow you to pass functions without needing to name them (i.e., anonymous functions), which leads to less variables floating around. They also allow you to delegate calling functions to _other _functions. Array methods, such as forEach(), map(), and filter(), take advantage of callbacks to execute functions onto a given array's elements. Feel free to check out the list of other array methods listed below.

Now that we know that functions in JavaScript can have access to many different types of variables in its list of arguments --_what else _is available for functions to use? That is, what is included in the scope of a function? Let's find out in the next section!

Further Research

results matching ""

    No results matching ""