Importance of functions

JavaScript programs are driven by functions. They're at the core of any significant application you'll make, and as you'll soon see in later sections of this course, they dominate other data types and features of the language--objects, arrays, classes and lots more.

Repetition of normal functions

So with a need to use functions so often, we'll be making functions a great deal. There's lots of repetition, due to the fact that you have to have to explicitly declare that you’re creating a function with the function keyword, add curly braces to signify the body, use return statements to end the function, and so on.

However, since ES2015, we have another option, the arrow function. It makes writing functions easier than ever with significant less code.

Arrow functions are named as such due to how they are written, using what's known as a fat arrow syntax, which looks like this =>

The great benefit of arrow functions strip away as much extraneous information as possible. But that's not the only benefit as you'll see later in this section, but in this one, we're going to look at the different patterns that arrow functions are used in JavaScript.

Syntax of arrow function

As it turns out, we no longer need much at all to write a function. We no longer need the function keyword, parentheses around parameters, the return keyword or curly braces. All you need is the following syntax, called the fat arrow:

 =>

Since it looks somewhat like an arrow, that's why we call them arrow functions.

[Why do arrow functions offer over normal functions?]

And arrow functions were added to JavaScript for two reasons:

  1. As we mentioned already, to provide a more concise way for creating functions.
  2. To make working with objects and classes easier due to how it handles the this keyword. Set that aside for now: we’ll touch on this benefits in object and class sections.

So let's start with what we know, normal functions with the function keyword. And in this example, let's say we have some user data in our app, including their name and maybe when they joined.

const user = {
  name: "john",
  joined: "May 12th, 2013",
};

But we want to better format their name. Say for example, we want to make sure their name is capitalized. We can create a helper function that capitalizes their name, which we'll call capitalizeName:

We'll use template literals here and it's going to take name's first character and convert it to uppercase to make it capital. And then using the slice string method after the first character, we concatenate the rest of the string. Don't be too concerned if it doesn't make complete sense.

Just know it takes a string and capitalizes it.

​ ​functioncapitalizeName(name) {
​ ​  return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;}

And we've already covered that we can directly put the identifier, the name on function declarations, right after the function keyword. So here, the word capitalize is part of our function.

But we know that there's an alternative syntax to making functions. We can make capitalizeName into a function expression just by setting it equal to a variable. Let's create a variable called just capitalize. So everything's the same, but we no longer call the function using it's original name, but using the variable identifier.

const capitalize = function capitalizeName(name) {
   return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}

And since we're not using the original function name, but the variable name, we can drop it. And now the original function is an anonymous function.

const capitalize = function(name) {
   return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}

So now we're in the position to switch over to arrow functions. Arrow functions use a very similar syntax:

  1. They are all function expressions and therefore assigned to a variable, and
  2. All arrow functions are anonymous. In fact, you can't directly give them a name.

Converting a normal function to arrow function

So here's how to convert our regular functions declared with the function keyword over to arrow functions.

First of all, we can drop the function keyword entirely. Arrow functions don't use any keyword, which make them incredibly easy to write:

const capitalize = (name) {
   return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}

Next, the core of making an arrow function is to now add what we mentioned earlier, the fat arrow. An equals sign and then a greater than arrow, which points towards our function body:

const capitalize = (name) => {
   return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}

So now we have a totally valid arrow function here. We can use exactly the same curly brace syntax for the body as before. Take a screenshot of this and know that we could stop right here. To make an arrow function we just need those two things:

  1. No function keyword, and
  2. A fat arrow after the parameters, and pointing towards the body

But what's neat about arrow functions is that if we have a short, simple, function as we do here, we can use a few shorthands to make our code even more terse.

The first thing we can do is that if we have only one parameter, as we do here, we can drop the parentheses entirely around the parameter, like so:

const capitalize = name => {
   return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}

But note that if ever we have more than one parameter, we need to add back our parentheses, otherwise we'll get an error. For example, if we had a second parameter, say age, we would need parentheses around both:

const capitalize = (name, age) => {
   return`${name.charAt(0).toUpperCase()}${name.slice(1)}`;
}

And the next shorthand we can use is if our function body is short enough, we can make use of what's known as an implicit return.

So right now, to return our capitalized name from our function, we need this return keyword, otherwise it won't work properly. Say we call capitalize with the name mark and console.log it:

const capitalize = (name) => {
  `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
};
console.log(capitalize("mark"));

[Run code] We get the default return value of functions, which we know is undefined. This will only work right with our return keyword in other words, an explicit return:

const capitalize = (name) => {
  return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;
};
console.log(capitalize("mark"));

[Run code] But with arrow functions, we don't have to use the curly braces syntax for writing a function body as we have with normal functions. So as long as we have a fat arrow here, any code after it will be interpreted as the function body.

And if we do remove them, but keep our return keyword and try running our code:

const capitalize = name =>
  return `${name.charAt(0).toUpperCase()}${name.slice(1)}`;

console.log(capitalize('mark'))

[Run code] We get an error saying Uncaught token return. So note that the explicit return keyword doesn't work with arrow functions. The reason for this is the implicit return of arrow functions. Which means that by default, if we don't have a curly brace function body with an arrow function, just the arrow, the resolved value within the body is always returned.

So all we have to do to use our implicit return is simply remove the return keyword.

const capitalize = (name) => `${name.charAt(0).toUpperCase()}${name.slice(1)}`;

console.log(capitalize("mark"));

And capitalize works properly by returning the capitalized name. So fat arrow function bodies make the return keyword unnecessary. And note that we don't have to have the function body be on the same line. We see here that it's on the next line, but the function works properly.

And the last shorthand we can use is to put the function body, that is the expression to be resolved and returned on the same line as the rest of the function:

const capitalize = (name) => `${name.charAt(0).toUpperCase()}${name.slice(1)}`;

console.log(capitalize("mark"));

[Run code] And this still works. So now we have a very concise and practical function, written all on a single line.

Arrow functions have really transformed JavaScript functions, especially in their appearance. If they look strange now, that's okay. We'll get lots of experience with them throughout this course. But once you get comfortable with them and start to replace normal functions with them, you'll really see how much it can clean up your code.

Arrow functions with higher order functions

And to see how arrow functions will be used the majority of the time going forward, we're going to take a look at a new pattern named callback functions.

When we looked into closures, we saw that functions could be used just like any other value in JS. As a result, we saw how functions could be returned from other functions.

And similarly if functions can be passed around like other values, functions can be passed to other functions. This is the basis of what we know as callback functions, and will be used a great deal when we move onto arrays and async functions in particular.

So with our capitalize function we want to use it in combination with a function that greets our user. And to greet our user, we want to have the user's capitalize name.

We'll call this function greetUser. And to see the contrast between normal and arrow functions, we'll make greetUser just a regular function with the function keyword:

​ ​functiongreetUser() {}

And this function is going to return our greeting using our capitalize function and the name we want to capitalize. So we'll provide name as a parameter to user and pass it to capitalize for it to do it's job:

function greetUser(name) {
  capitalize(name);
}

And then for the second argument to greetUser, we can provide a function. And this function, our callback, is going to take the result of capitalize, it's going to take the capitalized name as use it as an argument for itself to return a greeting. So we'll make another parameter called callback and accept the capitalized name. Which we'll return from greetUser to get the greeting:

function greetUser(name, callback) {
  return callback(capitalize(name));
}

So now all that's left to do is call greetUser and create our callback. So we'll pass greetUser our user name as the first argument from user.name. But what about this callback function?

greetUser(user.name);

Now we can make more sense of the name. So let's talk about terminology and everything that we're doing here. First of all---callback. Why in the world is it called a callback?

Don't be confused by the name. A callback function is just a function that is called after another function. And this is why it's named that way. Callback. It is 'called back' from the function it was used in. Callbacks make sure that we call one function after another function. That's all.

And if this doesn't make much sense now, it will when we get to async functions. However this ability to pass a function to another function as a callback has its own special name. It is called a higher-order function. It just means we are passing a function to another function. As a result, all callback functions are higher order functions.

So with terminology out of the way, we now know that since we have a callback, we can create a function as a function argument and then it will be called back in the function that it was referenced and executed in.

For this callback to create our greeting, we could create a normal JS function like so:

greetUser(user.name, function () {});

But in my view, it's a bit harder to read. Instead, we could just use a simple arrow function. So take a second and convert this anonymous function to an arrow function...

greetUser(user.name, (name) => {});

What data does function receive. Let's look back at where we call the callback. And it receives the result of the capitalize function, which a name, so we'll add one parameter to the callback, name. And then within the callback's body, we can do with the value whatever we like. But since we'll be greeting the user, we can say with template literals, hi there, name!, making sure to return the value at the end. Why? Because we're returning the result of this callback from greetUser:

greetUser(user.name, (name) => {
  return `Hi there, ${name}!`;
});

So let's first see that this works with a console log:

const result = greetUser(user.name, (name) => {
  return `Hi there, ${name}!`;
});
console.log(result);

[Run code] And we get our greeting with our user's capitalized name. Great.

However, we can make this even better using our shorthands. Take a minute and go back in the video if you need to recall all of the ways that we can make this callback shorter and apply them...

So first we can start with the parameters. We only have one, so we can remove the parentheses.

const result = greetUser(user.name, (name) => {
  return `Hi there, ${name}!`;
});
console.log(result);

Next, we can remove the traditional function body with curly braces. And now since the result of the body is implicitly returned, we don't need the return keyword:

const result = greetUser(user.name, name =>
  `Hi there, ${name}!`;
);
console.log(result);

And finally, we can put the entire callback and therefore the function call on the same line:

const result = greetUser(user.name, (name) => `Hi there, ${name}!`);
console.log(result);

[Run code] And if we run this again, it still works, but in a much shorter format.

Review

So arrow functions have a great power to make our code shorter and more readable by providing less code to read in general. Make use of these additional shorthands if it adds to your readability, and additionally we also took a look at how arrow functions are used a great deal of the time, particularly with array methods and async functions.

And through all of this additional work with functions, understanding callbacks and creating your own higher order functions, you're a step ahead other developers in your function mastery and knowing how these built in JS operations work behind the scenes.

Get comfortable with arrow functions, because we're going to see how to deep deeper in function concepts by using them to make even more higher order functions.