JavaScript Functions

Posted on April 22, 2020

Written by Preston Lamb

javascript

tldr;

Every JavaScript application uses functions. In this blog post we’ll go over the basics of functions and arrow functions, providing default parameters to functions, passing named parameters to functions, and using the rest parameters in functions. By the end of this blog post, you should be comfortable with each of these parts of dealing with JavaScript functions. Everything we cover in this blog post can be used in modern browsers today.

Named and Anonymouse Functions

Let’s start with the most simple way to write a function. Here’s an example:

function myFunction(arg1) {
	// Do something
}

Each named function starts with the function keyword. After that comes the name of the function. This is how you’ll reference and call the function later on. From the example above, the name would be myFunction. After the function name comes the argument list, enclosed in parentheses. The arguments, also called parameters, are optional but the parentheses are not. Then the function body starts and ends with curly braces. Inside the body of the function is where you’ll do your work. To call a function, you use its name, open parentheses, the list of arguments, and close parentheses.

myFunction('hello');

Another way you can create a function is by assigning an anonymous function to a variable. An anonymous function is a normal function but it doesn’t have a name. You would only use these as callbacks or by assigning them to a variable. Let’s look at an example of assigning an anonymous function to a variable.

const myFunction = function (arg1) {
	// Do something
};
myFunction();

You can call it in the same way too, as seen above. These are both valid ways of creating functions. In my experience, named functions are more common than assigning an anonymous function to a variable.

You can also have functions as attributes on objects. These functions have a special name: methods. It’s really the same thing though, and you can call it a method or a function. Everyone will know what you’re talking about. Here’s an example of a method and how to use it:

function sayHi() {
	console.log(`Hi my name is ${this.name}.`);
}
const person = {
	name: 'Preston Lamb',
	sayHi,
};
person.sayHi();

// or

const person = {
	name: 'Preston Lamb',
	sayHi: function () {
		console.log(`Hi my name is ${this.name}.`);
	},
};
person.sayHi();

// or

const person = {
	name: 'Preston Lamb',
	sayHi() {
		console.log(`Hi my name is ${this.name}.`);
	},
};
person.sayHi();

All three of these examples have the same result and are valid ways to add methods to an object. We’ll talk about arrow functions as methods next.

Arrow Functions

Another type of function that’s common in JavaScript is a lambda function, or arrow function as it’s more commonly known. Arrow functions are especially great when being used as a callback, but you don’t only have to use them as callbacks. They’re quick to write and easy to use. Let’s take a look at how you can use arrow functions.

const sayHi = () => console.log('Hi!');
sayHi(); // Hi!

const arr = ['Item 1', 'Item 2'];
arr.forEach((item) => console.log(item)); // Item 1 Item 2

const person = {
	name: 'Preston Lamb',
	doSomething: () => console.log('Does something');
}
person.doSomething(); // Does something

Arrow functions do not have their own context, so in the last example they have no access to this. To learn more, reference my article on the this keyword in JavaScript.

Arrow functions are great, and I normally reach for them mainly as callbacks. I don’t personally use them as the first example above, although it’s perfectly valid. If you don’t need any specific context in the function, an arrow function is a great option.

Providing Default Parameters to Functions

Another great feature of functions in ES6 is default function parameters. Default parameters makes it so that a parameter value is initialized with some value, but this initialized value will be overwritten by whatever is passed in to the function. This makes it so that there’s always some value in the parameter of a function; they won’t be undefined unless explicitly passed that way. Let’s look at an example where this would be helpful.

function divide(top, bottom) {
	console.log(top / bottom);
}
divide(5); // NaN

Here we have a divide function that takes two numbers and returns the top divided by the bottom. When we call it in the above manner, without the second parameter, the result is NaN. That’s because the bottom parameter is undefined since nothing was passed in to the function. Then maybe we forget to check what type the return value of this function is elsewhere in our code, and we assume it’ll always be a number. But in this case we’d get an error elsewhere because NaN is not a number. So to prevent the error, we have to do checks on the result anywhere we may call the function. It’d be best if we never ran in to this problem though, right? So let’s provide some default parameters to this function.

function divide(top, bottom = 1) {
	console.log(top / bottom);
}
divide(5); // 5

This time we call the function in the same way, but because we’ve provided a default parameter to the function for the bottom parameter, its value will always be 1 if nothing is passed in. So instead of getting NaN, we get 5 for our answer here.

This example is a little contrived, but here’s another example of something that I’ve run into in my job before. Let’s say we have a function that creates a new instance of an object with several attributes. Sometimes we want to give some starting values to the object, but other times we don’t. We can do this simply with default parameters.

function dog(startingValues = {}) {
	const dogObject = {
		name: startingValues.name || '',
		breed: startingValues.breed || '',
	};

	return dogObject;
}

const duke = dog();
console.log(duke); // { name: '', breed: '' }

const max = dog({ name: 'Max' });
console.log(max); // { name: 'Max', breed: '' }

Without the default parameters, if we didn’t pass an object to the dog function, we would get an error: cannot read property name of undefined. In this case, default parameters work perfectly to prevent us from having errors in our application.

You can have multiple default parameters in a function. Let’s say arguments two and three both have default parameters. If you want to use the default parameter for the second argument, but pass the third argument, you’re out of luck. You’d have to pass something as the second argument, even if it was null.

Named Parameters to Functions

Another convenient feature of parameters in ES6 functions is named parameters. Sometimes the parameter list of a function gets really long, and it can be hard to know what each parameter should be. If you wrote the function, you might remember. But only when you first wrote the function. Then each time you come back to it you’ll have to look again and try to remember what each does. Or, you could use named parameters. To use named parameters, all you’ll need to do is pass a single object to the function instead of multiple parameters. It’s similar to what we did in the last section. But instead of naming the variable (like we did above startingValues) we will have access to each of the attributes of the object as variables inside the function. Let’s take a look at this. We’ll first show an example without named parameters, and an example with.

function dogInfo(name, breed, age, foodPreference, likesToWalk) {
	return `The dog's name is ${name} and it is a ${breed}. It's age is ${age}, and it likes to eat ${foodPreference}. It ${
		likesToWalk ? 'does' : 'does not'
	} like to walk.`;
}
dogInfo('Duke', 'Labradoodle', '3 years', 'anything', true);

If we need to call this dogInfo function, we need to remember each one of these parameters, and more importantly the order has to be correct each time. Otherwise we’re not going to get the result we’re looking for. And looking at just the function above, what do some of those parameters actually mean? If I hadn’t written the function, I wouldn’t know what the true was for. Or the anything string. It can be really confusing, especially for other developers who look at your code. Now let’s try it with named parameters.

function dogInfo({ name, breed, age, foodPreference, likesToWalk }) {
	return `The dog's name is ${name} and it is a ${breed}. It's age is ${age}, and it likes to eat ${foodPreference}. It ${
		likesToWalk ? 'does' : 'does not'
	} like to walk.`;
}
dogInfo({ name: 'Duke', breed: 'Labradoodle', age: '3 years', likesToWalk: true, foodPreference: 'anything' });

This time, it’s very clear what each item is that we’re passing to the function. The object we pass in has the attribute of each right next to it. The other thing is that in this manner there is no order for the attributes on the object. No matter how you order them, it’s still going to work. That’s so much better than the first function! Also, your IDE should give you help in knowing what you should pass as well. Let’s now combine named parameters and default parameters.

function dogInfo({ name = 'Your Dog', breed = 'unknown', age, foodPreference, likesToWalk = true } = {}) {
	return `The dog's name is ${name} and it is a ${breed}. It's age is ${age}, and it likes to eat ${foodPreference}. It ${
		likesToWalk ? 'does' : 'does not'
	} like to walk.`;
}
dogInfo({ age: '3 years', foodPreference: 'anything' });

This time we only passed in the age attribute and the foodPreference, but due to default parameters we didn’t have any problems missing parameters, and we didn’t need to pass null as a parameter or anything like that. With named and default parameters, we can pass just the values we want and leave out the rest. This is really nice and really convenient.

Rest Parameters

The last thing we’ll talk about is the rest parameters option in a function. What this allows you to do is to name a certain number of parameters to the function, and then catch the rest of them in an array. Let’s look at an example of this.

function addition(setName, ...numbers) {
	const total = numbers.reduce((prev, next) => (prev += next), 0);
	return `Total of ${setName}: ${total}`;
}

const result = addition('years', 1, 2, 3, 4);
console.log(result); // Total of years: 10

What this function does is takes a parameter that is called setName, which is the name of the set of numbers we’re passing it. Then we capture the rest of the arguments passed to the function in the numbers array. It could be 0 to many arguments that are in that rest parameter. This is really convenient for when your function needs at least N arguments, but can use as many are as passed.

Now, this numbers parameter is different than the built in arguments attribute of a function. The biggest is that arguments includes all the arguments or parameters passed to a function. The rest parameter only includes all the parameters that weren’t named. The next big difference is that the rest parameter is actually an array. arguments is array-like, but you can’t use array methods (like forEach or reduce) on arguments. So you can use arguments, but it is different than a rest parameter.

Conclusion

Knowing all the ways to declare functions and use functions and parameters will allow you to write the most efficient applications possible. They’ll also be more user friendly, in the case of named parameters for example. A little bit of practice and a little bit of learning will make these things come more easily and second nature to you.

Click here to subscribe to the newsletter and be the notified when a new blog post is available!