Back to Basics: Functions, Hoisting, and Scope
Tags: beginners, functions, scope, javascript
Published: 2021-01-21 15:40:09

This series discusses the building blocks of JavaScript. Whether you're new to the language, you're preparing for a technical interview, or you're hoping to brush up on some key JS concepts, this series is for you.

Today's post is about functions:
- [What is a function?](#what-is-a-function)
- [Function declarations](#function-declarations)
- [Function expressions](#function-expressions)
  - [Arrow functions](#arrow-functions)
- [Calling functions](#calling-functions)
- [Hoisting](#hoisting)
- [Scope and closures](#scope-and-closures)
- [Resources](#resources)

## What is a function?

A function in JavaScript is a set of statements that take some input, do a certain task, and return some output.

When working with functions, you first have to **define** the function, which involves naming it and saying what actions it does. Then, it actually get those actions to happen, you have to **call** the function.

There are two main ways to define a function: **function declarations** and **function expressions**. (Note: there also is something called the [function constructor](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function), though it's less commonly used.)

## Function declarations
A function declaration, also known as a function definition or a function statement, is one way to define a function. It's structured like the following:

```
function name(input) {
  statements;
}
```

The name of the function is `name`. The `input` is the *parameters* for the function, and it's enclosed in parentheses. Inside of the curly brackets is `statements`, which do a certain task. `statements` often return a value, or an *output*. A function does not have to take in any parameters, so `input` is optional. The `statements` themselves are also optional (though that would just mean you'd have an empty function that didn't do anything).

For example, let's say we wanted to define a function using a function declaration that took in a number, and returned the given number times two:

```javascript
function double(number) {
  return number * 2;
}
```

In this example, `number` is passed to the function by **value** alone; in other words, this function does not change `number` in the larger, global context. To illustrate what that means, let's insert a few console logs before and after the above function:

```javascript
// declaring a variable called `count` and setting it equal to 3
let count = 3;
console.log(count); // 3

// declaring a function called `double` which returns an inputted number times 2
function double(number) {
  return number * 2;
}

// declaring a variable called `result` is set equal to calling the function `double` and passing the number `count` as the input
let result = double(count);
console.log(result); // 6

console.log(count); // 3
```

When we called the function `double()` and passed in `count`, we didn't change the value of `count` itself -- it still equaled `3`.

However, this is only true of *primitive parameters* in JavaScript. If you pass a *non-primitive parameter* to a function (such as an array or object), and the function alters the object in some way, then the object is changed outside of the function as well. For example:

```javascript
let fruits = ["apple", "banana", "orange"];

function removeLastElement(array) {
  array.pop();
  return array;
}

removeLastElement(fruits);

console.log(fruits); // ["apple", "banana"]
```

The above example uses the [`.pop()` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop), which removes the last element of an array. By passing in the `fruits` object as a parameter in `removeLastElement()`, the last element of `fruits` was removed, and the updated array was returned. When working with non-primitive values, it's important to keep in mind that passing them into functions may end up changing their value.

## Function expressions

Another way to define functions is with a function expression. The main difference between a function expression and function declaration is that with function expressions, the function name is optional. If you don't include a function name, you have an **anonymous function**. A function expression is structured like the following:

```
function name(input) {
    statements;
}
```

Note that this is the exact same structure as function declaration. The following is an example of an anonymous function, meaning that it does not have a name. The function is set equal to a variable called `triple`:

```javascript
const triple = function (number) {
  return number * 3;
};
```

Function expressions are often written as <a id="arrow-functions">**arrow functions**</a>. Arrow functions are considered to be compact versions of function expressions, and are often used to "clean up" code. Let's turn the above function into an arrow function:

```javascript
// Standard function expression
function (number) {
  return number * 3;
};

// Arrow function
number => number * 3;
```

What changed with the arrow function? The words `function` and `return` were removed, there are no parentheses around the parameter `number`, the curly brackets were replaced by an arrow `=>`, and everything is on one line.

However, these rules vary depending on the arrow function. If the function has only **one** parameter, then you don't surround it in parentheses. If it has **zero or 2+** parameters, then you do surround it in parentheses. If the function has only **one statement**, then you don't have the curly brackets or the word `return`. If the function has **more than one statement**, then you have **both** the brackets and the word `return`. Let's see an example of each of these:

```javascript
// One parameter, one statement
number => number * 3; // AB

// Zero parameters, one statement (these are often used in callback functions)
() => x * 2;

// Two parameters, one statement
(a, b) => a - b;

// Two parameters, multiple statements:
(a, b) => {
  let tax = 0.05;
  return (a + b) * tax;
};
```

Arrow functions have a lot of changing syntax, depending on the function. However, it's less important to memorize exactly when to use parentheses around an input, than it is to recognize what an arrow function generally looks like, and where to find more resources about it. Over time and with practice, you'll end up not needing to refer back to the documentation. This is true of a lot of aspects of programming: rather than trying to memorize every little detail of how something is written and the specific ways to use it, it's much better to recognize something and know where to go for more information. Every programmer uses Google and refers to documentation, no matter how long they've been doing it.

## Calling functions

Just because you defined a function, that doesn't mean the function has been executed. When you define a function, you say what it's called and what it's supposed to do. When you call a function, it actually gets performed.

To call a function, you refer to the function's name, and pass in arguments that correspond to the parameters. To call the function `triple()`, which we defined above, we need to refer to its name, and pass in one number as an argument:

```javascript
triple(5);
```

## Hoisting
**Hoisting** in JavaScript means that variable declarations and function declarations are brought to the top of the code.

This is tricky concept to grasp at first, so it can help to look at an example. Let's create a function using a function declaration, and call it `numberSquared`. `numberSquared()` will take an inputted number, and then console log that value squared. Then, after the function, we can call it, and we'll pass in the number `5`.

```javascript
function numberSquared(num) {
  console.log(num * num);
}

numberSquared(5);
```

The result of the above code is `25`.

Now, what would happen if we called the function *before* we declared the function?

```javascript
numberSquared(5);

function numberSquared(num) {
  console.log(num * num);
}
```

Again, the result of the above code is `25`. This is because the function declaration was brought to the top when your code was compiled.

Keep in mind that only function declarations, not function expressions, are hoisted.

## Scope and closures
A **scope** in JavaScript is what is currently "visible" or "accessible". According to [MDN documentation](https://developer.mozilla.org/en-US/docs/Glossary/Scope), "If a variable or other expression is not 'in the current scope,' then it is unavailable for use."

In terms of functions, the variables that are declared in the function are only accessible within the function. This is called a **closure**.

To see examples of different scopes, let's look at the following:

```javascript
const weather = "rainy";

function myNameAndTheWeather() {
  const name = "Alisa";

  console.log(name);
  console.log(weather);
}

myNameAndTheWeather();

console.log(weather);
console.log(name);
```

What would happen if we ran this program? The output would be the following:
```
Alisa
rainy
rainy
[ReferenceError: name is not defined]
```

To understand why these are the results, let's go over what the code says, and what happens when we run it. First, the variable `weather` is initialized and set equal to `"rainy"`. Then, using a function declaration, the function `myNameAndTheWeather()` is defined. Inside of `myNameAndTheWeather()`, the variable `name` is initialized and set equal to `"Alisa"`, `name` is console logged, and `weather` is console logged. Then, outside of the function, `myNameAndTheWeather()` is called. Then, `weather` is console logged. Finally, `name` is console logged.

When we run this program, the first thing that happens is that the function `myNameAndTheWeather()` is called. `name` is defined in the function, in the **local scope**, so the function is able to console log it. `weather` is defined outside of the function, in the **global scope**, so the function also has access to it. In other words, the function has access to variables declared in its own local scope (a closure) and in the global scope. Therefore, `Alisa` and `rainy` are logged to the console.

After `myNameAndTheWeather()` is executed, the program goes to the next line, which says to log `weather` to the console. `weather` is an accessible variable, so the program console logs its value. Finally, the program tries to console log the variable `name`. However, `name` is defined *within* the function `myNameAndTheWeather()`. It has a local scope, which means we don't have access to it from outside of the function. Therefore, a reference error is returned.

----

Let me know in the comments if you have any questions or other ways of thinking about functions, scope, and hoisting in JavaScript.


## Resources
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Functions
- https://developer.mozilla.org/en-US/docs/web/JavaScript/Reference/Operators/function
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
- https://developer.mozilla.org/en-US/docs/Glossary/Hoisting
- https://developer.mozilla.org/en-US/docs/Glossary/Scope