Functions

With practice, you can calculate the area of the rectangle without being instructed with these three steps every time.

We can calculate the area of one rectangle with the following code:

const width = 10;
const height = 6;
const area =  width * height;
console.log(area); // Output: 60

 

Imagine being asked to calculate the area of three different rectangles:

// Area of the first rectangle
const width1 = 10;
const height1 = 6;
const area1 =  width1 * height1;

// Area of the second rectangle
const width2 = 4;
const height2 = 9;
const area2 =  width2 * height2;

// Area of the third rectangle
const width3 = 10;
const height3 = 10;
const area3 =  width3 * height3;

 

In programming, we often use code to perform a specific task multiple times. Instead of rewriting the same code, we can group a block of code together and associate it with one task, then we can reuse that block of code whenever we need to perform the task again. We achieve this by creating a function. A function is a reusable block of code that groups together a sequence of statements to perform a specific task.

In this lesson, you will learn how to create and use functions, and how they can be used to create clearer and more concise code.

 

Function Declarations

In JavaScript, there are many ways to create a function. One way to create a function is by using a function declaration. Just like how a variable declaration binds a value to a variable name, a function declaration binds a function to a name, or an identifier. Take a look at the anatomy of a function declaration below:

A function declaration consists of:

  • The function keyword.
  • The name of the function, or its identifier, followed by parentheses.
  • A function body, or the block of statements required to perform a specific task, enclosed in the function’s curly brackets, { }.

A function declaration is a function that is bound to an identifier, or name. We should also be aware of the hoisting feature in JavaScript which allows access to function declarations before they’re defined.

Take a look at example of hoisting:

console.log(greetWorld()); // Output: Hello, World!

function greetWorld() {
  console.log(‘Hello, World!’);
}

 

Notice how hoisting allowed greetWorld() to be called before the greetWorld() function was defined! Since hoisting isn’t considered good practice, we simply want you to be aware of this feature.

If you want to read more about hoisting, check out MDN documentation on hoisting.

Calling a Function

A function declaration binds a function to an identifier. However, a function declaration does not ask the code inside the function body to run, it just declares the existence of the function. The code inside a function body runs, or executes, only when the function is called. To call a function in your code, you type the function name followed by parentheses.

This function call executes the function body, or all of the statements between the curly braces in the function declaration.

We can call the same function as many times as needed.

Let’s practice calling functions in our code.

Parameters and Arguments

So far, the functions we’ve created execute a task without an input. However, some functions can take inputs and use the inputs to perform a task. When declaring a function, we can specify its parameters. Parameters allow functions to accept input(s) and perform a task using the input(s). We use parameters as placeholders for information that will be passed to the function when it is called.

Let’s observe how to specify parameters in our function declaration:

The calculateArea(), computes the area of a rectangle, based on two inputs, width and height. The parameters are specified between the parentheses as width and height, and inside the function body, they act just like regular variables. width and height act as placeholders for values that will be multiplied together.

When calling a function that has parameters, we specify the values in the parentheses that follow the function name. The values that are passed to the function when it is called are called arguments. Arguments can be passed to the function as values or variables.

In the function call above, the number 10 is passed as the width and 6 is passed as height. Notice that the order in which arguments are passed and assigned follows the order that the parameters are declared.

The variables rectWidth and rectHeight are initialized with the values for the height and width of a rectangle before being used in the function call.

By using parameters, calculateArea() can be reused to compute the area of any rectangle! Functions are a powerful tool in computer programming so let’s practice creating and calling functions with parameters.

 

Default Parameters

One of the features added in ES6 is the ability to use default parameters. Default parameters allow parameters to have a predetermined value in case there is no argument passed into the function or if the argument is undefined when called.

Take a look at the code snippet below that uses a default parameter:

function greeting (name = ‘stranger’) {
  console.log(`Hello, ${name}!`)
}

greeting(‘Nick’) // Output: Hello, Nick!
greeting() // Output: Hello, stranger!

 

  • In the example above, we used the =operator to assign the parameter name a default value of ‘stranger’. This is useful to have in case we ever want to include a non-personalized default greeting!
  • When the code calls greeting(‘Nick’)the value of the argument is passed in and, ‘Nick’, will override the default parameter of ‘stranger’ to log ‘Hello, Nick!’ to the console.
  • When there isn’t an argument passed into greeting(), the default value of ‘stranger’ is used, and ‘Hello, stranger!’ is logged to the console.

By using a default parameter, we account for situations when an argument isn’t passed into a function that is expecting an argument.

Let’s practice creating functions that use default parameters.

Return

When a function is called, the computer will run through the function’s code and evaluate the result of calling the function. By default that resulting value is undefined.

function rectangleArea(width, height) {
  let area = width * height
}
console.log(rectangleArea(5, 7)) // Prints undefined

 

In the code example, we defined our function to calculate the area of a width and height parameter. Then rectangleArea() is invoked with the arguments 5 and 7. But when we went to print the results we got undefined. Did we write our function wrong? No! In fact, the function worked fine, and the computer did calculate the area as 35, but we didn’t capture it. So how can we do that? With the keyword return!

To pass back information from the function call, we use a return statement. To create a return statement, we use the return keyword followed by the value that we wish to return. Like we saw above, if the value is omitted, undefined is returned instead.

When a return statement is used in a function body, the execution of the function is stopped and the code that follows it will not be executed. Look at the example below:

function rectangleArea(width, height) {
  if (width < 0 || height < 0) {
    return ‘You need positive integers to calculate area!’;
  }
  return width * height;
}

 

If an argument for width or height is less than 0, then rectangleArea() will return ‘You need positive integers to calculate area!’. The second return statement width * height will not run.

The return keyword is powerful because it allows functions to produce an output. We can then save the output to a variable for later use.

Helper Functions

We can also use the return value of a function inside another function. These functions being called within another function are often referred to as helper functions. Since each function is carrying out a specific task, it makes our code easier to read and debug if necessary.

If we want to define a function that converts the temperature from Celsius to Fahrenheit, we could write two functions like:

function multiplyByNineFifths(number) {
  return number * (9/5);
};

function getFahrenheit(celsius) {
  return multiplyByNineFifths(celsius) + 32;
};

getFahrenheit(15); // Returns 59

 

In the example above:

  • getFahrenheit() is called and 15 is passed as an argument.
  • The code block inside of getFahrenheit() calls multiplyByNineFifths() and passes 15as an argument.
  • multiplyByNineFifths() takes the argument of 15 for the number parameter.
  • The code block inside of multiplyByNineFifths() function multiplies 15 by (9/5), which evaluates to 27.
  • 27 is returned back to the function call in getFahrenheit().
  • getFahrenheit() continues to execute. It adds 32 to 27, which evaluates to 59.
  • Finally, 59 is returned back to the function call getFahrenheit(15).

We can use functions to section off small bits of logic or tasks, then use them when we need to. Writing helper functions can help take large and difficult tasks and break them into smaller and more manageable tasks.

Function Expressions

Another way to define a function is to use a function expression. To define a function inside an expression, we can use the function keyword. In a function expression, the function name is usually omitted. A function with no name is called an anonymous function. A function expression is often stored in a variable in order to refer to it.

Consider the following function expression:

To declare a function expression:

  1. Declare a variable to make the variable’s name be the name, or identifier, of your function. Since the release of ES6, it is common practice to use const as the keyword to declare the variable.
  2. Assign as that variable’s value an anonymous function created by using the function keyword followed by a set of parentheses with possible parameters. Then a set of curly braces that contain the function body.

To invoke a function expression, write the name of the variable in which the function is stored followed by parentheses enclosing any arguments being passed into the function.

variableName(argument1, argument2)

 

Unlike function declarations, function expressions are not hoisted so they cannot be called before they are defined.

Let’s define a new function using a function expression.

Arrow Functions

ES6 introduced arrow function syntax, a shorter way to write functions by using the special “fat arrow” () => notation.

Arrow functions remove the need to type out the keyword function every time you need to create a function. Instead, you first include the parameters inside the ( ) and then add an arrow => that points to the function body surrounded in { } like this:

const rectangleArea = (width, height) => {
  let area = width * height;
  return area;
};

 

It’s important to be familiar with the multiple ways of writing functions because you will come across each of these when reading other JavaScript code.

Concise Body Arrow Functions

JavaScript also provides several ways to refactor arrow function syntax. The most condensed form of the function is known as concise body. We’ll explore a few of these techniques below:

  1. Functions that take only a single parameter do not need that parameter to be enclosed in parentheses. However, if a function takes zero or multiple parameters, parentheses are required.
  2. A function body composed of a single-line block does not need curly braces. Without the curly braces, whatever that line evaluates will be automatically returned. The contents of the block should immediately follow the arrow => and the return keyword can be removed. This is referred to as implicit return.

 

So if we have a function:

const squareNum = (num) => {
  return num * num;
};

 

We can refactor the function to:

const squareNum = num => num * num;

 

Notice the following changes:

  • The parentheses around num have been removed, since it has a single parameter.
  • The curly braces { } have been removed since the function consists of a single-line block.
  • The return keyword has been removed since the function consists of a single-line block.

Scope

An important idea in programming is scope. Scope defines where variables can be accessed or referenced. While some variables can be accessed from anywhere within a program, other variables may only be available in a specific context.

You can think of scope like the view of the night sky from your window. Everyone who lives on the planet Earth is in the global scope of the stars. The stars are accessible globally. Meanwhile, if you live in a city, you may see the city skyline or the river. The skyline and river are only accessible locally in your city, but you can still see the stars that are available globally.

Blocks and Scope

Before we talk more about scope, we first need to talk about blocks.

We’ve seen blocks used before in functions and if statements. A block is the code found inside a set of curly braces {}. Blocks help us group one or more statements together and serve as an important structural marker for our code.

A block of code could be a function, like this:

const logSkyColor = () => {
  let color = ‘blue’;
  console.log(color); // blue
};

 

Notice that the function body is actually a block of code.

Observe the block in an if statement:

if (dusk) {
  let color = ‘pink’;
  console.log(color); // pink
};

 

Global Scope

Scope is the context in which our variables are declared. We think about scope in relation to blocks because variables can exist either outside of or within these blocks.

In global scope, variables are declared outside of blocks. These variables are called global variables. Because global variables are not bound inside a block, they can be accessed by any code in the program, including code in blocks.

Let’s take a look at an example of global scope:

const color = ‘blue’

const returnSkyColor = () => {
  return color; // blue
};

console.log(returnSkyColor()); // blue

 

 

  • Even though the color variable is defined outside of the block, it can be accessed in the function block, giving it global scope.
  • In turn, color can be accessed within the returnSkyColor function block.

 

Let’s work with global variables to see how data can be accessible from any place within a program.

Block Scope

The next context we’ll cover is block scope. When a variable is defined inside a block, it is only accessible to the code within the curly braces {}. We say that variable has block scope because it is only accessible to the lines of code within that block.

Variables that are declared with block scope are known as local variables because they are only available to the code that is part of the same block.

Block scope works like this:

const logSkyColor = () => {
  let color = ‘blue’;
  console.log(color); // blue
};

logSkyColor(); // blue
console.log(color); // ReferenceError

 

You’ll notice:

 

  • We define a function logSkyColor().
  • Within the function, the color variable is only available within the curly braces of the function.
  • If we try to log the same variable outside the function, throws a ReferenceError.

 

Scope Pollution

It may seem like a great idea to always make your variables accessible, but having too many global variables can cause problems in a program.

When you declare global variables, they go to the global namespace. The global namespace allows the variables to be accessible from anywhere in the program. These variables remain there until the program finishes which means our global namespace can fill up really quickly.

Scope pollution is when we have too many global variables that exist in the global namespace, or when we reuse variables across different scopes. Scope pollution makes it difficult to keep track of our different variables and sets us up for potential accidents. For example, globally scoped variables can collide with other variables that are more locally scoped, causing unexpected behavior in our code.

Let’s look at an example of scope pollution in practice so we know how to avoid it:

let num = 50;

const logNum = () => {
  num = 100; // Take note of this line of code
  console.log(num);
};

logNum(); // Prints 100
console.log(num); // Prints 100

 

You’ll notice:

 

  • We have a variable num.
  • Inside the function body of logNum(), we want to declare a new variable but forgot to use the let keyword.
  • When we call logNum(), num gets reassigned to 100.
  • The reassignment inside logNum()affects the global variable num.
  • Even though the reassignment is allowed and we won’t get an error, if we decided to use num later, we’ll unknowingly use the new value of num.

 

While it’s important to know what global scope is, it’s best practice to not define variables in the global scope.

Practice Good Scoping

Given the challenges with global variables and scope pollution, we should follow best practices for scoping our variables as tightly as possible using block scope.

Tightly scoping your variables will greatly improve your code in several ways:

  • It will make your code more legible since the blocks will organize your code into discrete sections.
  • It makes your code more understandable since it clarifies which variables are associated with different parts of the program rather than having to keep track of them line after line!
  • It’s easier to maintain your code, since your code will be modular.
  • It will save memory in your code because it will cease to exist after the block finishes running.

Here’s another example of how to use block scope, as defined within an if block:

const logSkyColor = () => {
  const dusk = true;
  let color = ‘blue’;
  if (dusk) {
    let color = ‘pink’;
    console.log(color); // pink
  }
  console.log(color); // blue
};

console.log(color); // ReferenceError

 

Here, you’ll notice:

 

  • We create a variable dusk inside the logSkyColor() function.
  • After the if statement, we define a new code block with the {} braces. Here we assign a new value to the variable color if the if statement is truthy.
  • Within the if block, the color variable holds the value ‘pink’, though outside the if block, in the function body, the color variable holds the value ‘blue’.

 

  • While we use block scope, we still pollute our namespace by reusing the same variable name twice. A better practice would be to rename the variable inside the block.

Block scope is a powerful tool in JavaScript, since it allows us to define variables with precision, and not pollute the global namespace. If a variable does not need to exist outside a block— it shouldn’t!

Review

In this lesson, we covered some important concepts about functions:

  • A function is a reusable block of code that groups together a sequence of statements to perform a specific task.
  • A function declaration :
  • A parameter is a named variable inside a function’s block which will be assigned the value of the argument passed in when the function is invoked:
  • To call a function in your code:
  • ES6 introduces new ways of handling arbitrary parameters through default parameters which allow us to assign a default value to a parameter in case no argument is passed into the function.
  • To return a value from a function, we use a return statement.
  • To define a function using function expressions:
  • To define a function using arrow function notation:
  • Function definition can be made concise using concise arrow notation:

Let’s review the following terms:

    • Scope is the idea in programming that some variables are accessible/inaccessible from other parts of the program.

 

  • Blocks are statements that exist within curly braces {}.

 

  • Global scope refers to the context within which variables are accessible to every part of the program.
  • Global variables are variables that exist within global scope.
  • Block scope refers to the context within which variables that are accessible only within the block they are defined.
  • Local variables are variables that exist within block scope.
  • Global namespace is the space in our code that contains globally scoped information.
  • Scope pollution is when too many variables exist in a namespace or variable names are reused.

It’s good to be aware of the differences between function expressions, arrow functions, and function declarations. As you program more in JavaScript, you’ll see a wide variety of how these function types are used.

As you continue your coding journey, remember to use best practices when declaring your variables! Scoping your variables tightly will ensure that your code has clean, organized, and modular logic.