Writing Reusable Code

Functions allow us to group together bits of code so that we can easily reuse it.

Let's imagine for example we had the following code:

const x = 1;
const y = 2;
// Will log out 3
const total = x + y;
console.log( total );

This seems pretty straight forward, right? We want to get two numbers and add them together.

But what if our application got more complicated and we had something like this:

const a = 1;
const b = 2;
const c = 3;
const d = 4;
// These all repeat the adding process
const total1 = a + b;
const total2 = c + d;
const total = total1 + total2;
// Logs out 10
console.log( total );

Although this code looks okay, it breaks a cardinal rule of programming: Do Not Repeat Yourself.

"DRY " or "Do not Repeat Yourself" is a programming concept that tells us when we see repeated code, we want to simplify, usually using functions.

So, we could rewrite this code using a function like this:

const a = 1;
const b = 2;
const c = 3;
const d = 4;
function addNums( x, y ) {
return x + y;
}
// These all repeat the adding process
const total1 = addNums(a, b);
const total2 = addNums(c, d);
const total = addNums(total1, total2);
// Logs out 10
console.log( total );

Although our code is actually a bit longer now, this demonstrates how we will use functions and why.

Whenever we have a repeatable piece of code, we wrap it in a function for easy reuse. This gives us an added benefit that if we ever need to change our addition process, we would only need to update the operation in once place: inside our addNums() function.

Now, let's break functions down a bit more so that we can understand their various parts.

Function Declarations and Function Calls

When we setup a function for use, it is called "declaring a function." Like most things in JavaScript, there are several ways we can declare a function. However, this is the most basic:

// Start with keyword function
// Then give the function a name
// Follow the name with parathesis (we will leave blank for now)
// Then open and closing curly braces (this is the function body)
// The code we want to repeat goes inside of the curly braces
function sayHi() {
console.log( "Hi!" );
}

This function is very simple. All it will do is log out the message "Hi!" in the console.

Defining a function makes it available for use. But we must "call the function" in order for it to execute the code inside of the curly braces.

We call a function like this:

// Will log out "Hi!"
sayHi();

Notice that we have the function name and then opening and closing parenthesis.

If we want to reuse a function and have it execute multiple times, we can simply call it however many times we want:

// Will log out "Hi!"
sayHi();
// Will log out "Hi!" again
sayHi();
// And again
sayHi();

Now functions like this will get pretty boring and not very helpful because they give us the exact same output every time. It is also possible to pass in different values to a function so it can produce different output based on what you give it.

Function Parameters

When we pass a value in to a function to use, we call that value a parameter. A parameter is like a variable that only a function can use.

function sayHi( name ) {
console.log( `Hi ${name}!` );
}
// Will log out "Hi Zac!"
sayHi( "Zac" );

The flow here can be a little confusing at first. Here is what is happening:

  1. We setup our function to accept a variable called "name"

  2. Then the function will log out a message with that variable

  3. When we call the function we have to pass in a value for name to equal

Here are a couple more examples of functions with parameters to give you a feel for what they look like:

// Will log out "Bye zgordon"
function sayBye( username ) {
console.log( `Bye ${username}!` );
}
sayBye( "zgordon" );
// Will log out 8
function subtract( x, y) {
console.log( x - y );
}
subtract( 10, 2 );
// Will log out 4
const numToRound = 3.4;
function roundUp( num ) {
console.log( Math.ceil( num ) );
}
roundUp( numToRound );

Notice in the last example we did not hard code the value of the parameter when we called the function. Instead we passed in a variable as the value. This is totally acceptable and common when passing parameters to functions.

Default Parameters

Sometimes we want a function to work whether we pass it a parameter or not. Or maybe we want it to have a default value as a backup.

We can do that like so:

function sayHi( message = `Hi!` ) {
console.log( message );
}
// Will log "Hi!"
sayHi();
// Will log "Howdy!"
sayHi( "Howdy!" );

Notice in the above example, if we do not pass in a parameter value for message it will automatically default to "Hi!" like we set it to.

However, we can also override this default value if we pass in a new parameter value.

Function Scope & Global Scope

The term "Scope" in programming refers to when you have access to certain values.

Because functions are isolated from the rest from the code outside their curly braces, they have a level of scope not accessible outside of the function.

Let's take a look at this example code to see global and function scope in action:

const inGlobalScope = `Get me anywhere!`;
function sayHi( message = `Hi!` ) {
const inFunctionScope = `Only available in this function!`
console.log( inGlobalScope, message, inFunctionScope );
}
// Has access to all variables
sayHi();
// Gives an error for message and inFunctionScope
console.log( inGlobalScope, message, inFunctionScope );

The important take away from this is the following:

  1. Variables assigned outside of functions, CAN be accessed inside of functions

  2. Variables assigned inside of a function, CANNOT be accessed outside of the function

  3. Parameters count as being defined inside a function and CANNOT be accessed outside of a function

Scope can get far more complex in JavaScript, but these three rules are enough for us for now.

Returning Values from Functions

All functions return a value.

What that means is if we call a function and assign the value to a variable we are going to get something.

function sayHi() {
console.log("Hi!");
}
// Logs out "Hi!" and
// Assigns function call to a variable
const hi = sayHi();
// Logs undefined
console.log(hi);

Now in this example we are getting undefined for the value of sayHi(). That is because we are not implicitly returning anything from our function. So functions that don't return anything will give you the value of undefined.

I want to point out that on line 6 the function is still going to execute. So you will get "Hi!" logged to the screen. But it will also do an extra step and save any returned values from the function to the variable.

Now, here is an example where we are going to manually control what we return from the function.

function getHiMessage() {
return "Hi!";
}
// Assigns what the function returns to hi
const hiMessage = getHiMessage();
// Logs "Hi!"
console.log(hiMessage);

This is a very common pattern for functions. Sometimes we want a function to do something, like log a message to the console. Other times we want a function to return us a value, like a calculation or a welcome message in this case.

When you name a function that returns something it is common to start the name with getSomething. Starting a function name with get tells the user just in the function name that we are going to return something.

Arrow Function Syntax

There is a another way of writing functions that is helpful to know about. This is called the Arrow Syntax or Arrow Functions or "Fat Arrow Functions."

Here is what the syntax looks like for creating a function in this way alongside creating a function in the normal way:

// Normal function syntax
function sayHi( name ) {
console.log( name );
}
// Logs Zac
sayHi( "Zac" );
// Arrow function syntax
const sayHello = ( name ) => {
console.log( name );
}
// Logs Zac
sayHello( "Zac" );

These two functions accomplish the same result. They both log out the name passed into them.

If all we have is one parameter we can also leave off the parenthesis around the parameter. However, the parenthesis are still needed for passing multiple parameters:

// Single parameter does not need parenthesis
const sayHello = name => {
console.log( name );
}
// Multiple parameters do need parenthesis
const sayHello = ( first, last ) => {
console.log( `Hello ${first} ${last}` );
}

If all we need is a function to return something to us, there is a shorter syntax for arrow functions as well:

// Full arrow syntax
const getHelloMessage = ( name ) => {
return `Hello ${name}!`;
}
// Shortened arrow syntax
// For when you only have to return a value
const getHelloMessage = ( name ) => `Hello ${name}`;

The rule here is if you leave off the curly braces an arrow function will simply return the value.

Although we have to learn that arrow functions exist, they are not completely the same as regular functions. The differences are a bit advanced, but in part have to do with arrow functions not recognizing the keyword this which is a bit of an advanced topic for us at the moment.

The reason we are learning about arrow functions at this point is because you will likely see them in real world code. We will stay away from using them too much until we understand more about JavaScript and can fully understand the difference between arrow functions and normal functions. But the syntax is quite different, so they are helpful to know about.

When Do We Write Functions?

The general rule of thumb is, any time you have a bit of code that you need to reuse, it should go in a function. This let's us follow the DRY rule in programming of not repeating code.

Here is a super simple example of this:

const first = "Zac";
const last = "Gordon";
// Pretend some code is here
console.log( `Hello ${first} ${last}` );
// Pretend even
// more code
// goes here
console.log( `Hello ${first} ${last}` );
// And then finally there is more code
// that goes here
console.log( `Hello ${first} ${last}` );

In this example we above we see that we repeat the logging of a great several times in our code. This is a great example of when we would break out the repeated code into its own function like this:

const first = "Zac";
const last = "Gordon";
function sayHello( first, last ) {
console.log( `Hello ${first} ${last}` );
}
// Pretend some code is here
sayHello( first, last );
// Pretend even
// more code
// goes here
sayHello( first, last );
// And then finally there is more code
// that goes here
sayHello( first, last );

Now, if we ever need to change what the hello message does or says, we only have to change it in one place.

Another helpful guideline for writing functions is that a single function should do one single thing. If your function starts to do a bunch of things, it likely means you want to break your function up into smaller function then have one larger function that calls those smaller function.

In this example below we can see that our single function is doing quite a lot:

function renderSite() {
// Pretend we get some header information
// Then we log out the header
console.log( `Header logged out` );
// Then we get some main content information
// And log out the main content
console.log( `Content logged out` );
// And finally we get some footer information
// And log out the footer
console.log( `Footer logged out` );
}

This would be a good example of when we would take smaller part from our large function and break it into smaller functions. Then our loadSite() function would call all those smaller functions.

function renderSite() {
renderHeader();
renderContent();
renderFooter();
}
function renderHeader() {
// Pretend we get some header information
// Then we log out the header
console.log( `Header logged out` );
}
function renderContent() {
// Then we get some main content information
// And log out the main content
console.log( `Content logged out` );
}
function renderFooter() {
// And finally we get some footer information
// And log out the footer
console.log( `Footer logged out` );
}

You can see here that we have taken little bits of code that accomplish specific tasks, like render the Header, Content or Footer, and we broke them out into their own functions.

Then in our primary function we simply call the other functions.

While both examples do the exact same thing, hopefully you can see that the second example looks better, is easier to read and understand, and will ultimately be a little easier to maintain and troubleshoot.

Next Steps