Book Club: Eloquent JavaScript - Chapter 3
Tags: bookclub, javascript, beginners, webdev
Published: 2021-08-10 16:31:10

## The World of Functions

### Quick update

It's been almost a month since I published the first part of the Eloquent JavaScript Book Club series. I enjoyed the feedback I received from the Dev.to community and was happy to see folks wanting to join the book club and read along. However, as it has been almost a month, I'm sure many of you continued and finished the book without me. It's cool; I get it. Personally, a lot has happened in my life.

I quit my current job and am very happy to have accepted an excellent offer at a great company. I received an unconditional offer for a Masters in Computer Science & Artificial Intelligence, where I'll be studying part-time for the next two years. I learned a heck load of data structures, algorithms, systems design, and everything in between.

It's been a wild month, but I'm ready to sit back a bit. Drink a nice cold brew. Open up the dusty textbook and get into some _eloquent JavaScript_. Before we start, I want to quickly mention that I also completed the [Just JavaScript](https://justjavascript.com/) book/workshop/mini-course by Dan Abramov. I've already seen some parallels between eloquent JS and that course. I would wholeheartedly recommend getting into both. It helped solidify my mental model of how things like variables (and functions) work. There should be a blog post to analyse the two texts.

Right, **functions**.

> People think that computer science is the art of geniuses, but the actual reality is the opposite, just many people doing things that build on each other, like a wall of mini stones.

<aside>- Donald Knuth</aside>

Without functions, our code wouldn't function well. It will still do its job. Expressions and statements will continue to execute whilst our bindings (variables) will continue latching onto data. But without some order or a way of keeping related code together, it'd be complicated to manage.

We can create functions with a function expression. It's similar to how we have defined variables.

```js
const addTwo = function (num) {
  return num + 2;
};
```

The `num` is a _parameter_, whilst the curly braces encapsulate the _body_ of the function. The code above creates a constant called `addTwo` and binds it to a function that takes in a number and adds two to it.

Some functions have a return statement. Others return nothing at all. Yet just because it seems like it returns nothing in the code, in reality, all operations with no explicit return statement return `undefined.` Another example is to open your browser's console, and type in `console.log('hello')`. You'll see hello being printed, but you'll also get the type `undefined` returned. That's because the `.log()` is a function that doesn't return anything. It just runs a side effect, which is the printed message.

<aside>
  Side note, the same thing happens when we execute an expression like `let x =
  42;` as variable declarations do not produce a value but returns something.
</aside>

### Understanding Scope

A crucial advantage that a function has is being able to have it's own scope. It's a mechanism that allows a function to deal with its internal state and prevent other functions from manipulating state. It creates separation of scope, where you have the _global_ scope (outside the function), and the _inner_ scope. Global scope is like setting some variables at the top of your file.

```js
let time = 9;
let closingTime = 11;
```

Functions have the ability to read those variables, and even manipulate them (we will discuss why this is not necessarily good). However, we can't reach into functions and control the variables.

```js
const personalSchedule = function () {
  let doctorsAppointment = 12;
};

console.log(doctorsAppointment);
// doctorsAppointment is not defined
```

These variables are known as local variables (or local bindings). They only exist for a limited amount of time, when the function is called. Then, once the the function has finished executing, they cease to exist. It's quite melancholic.

A key thing to note is that variables declared with `let` or `const` are local to the **block** they are called in, and therefore can not be called outside the block, unlike `var`. A great example is a for loop:

```js
for (let i = 0; i < 5; i++) {
  // execute code
}
console.log(i); // undefined

for (var i = 0; i < 5; i++) {
  // execute code
}
console.log(i); // 5
```

<aside>Notice the difference in the highlights</aside>

Another thing to note is that whilst we can't _look inside_ a function to get its variables, we can look outside the scope of the function.

```js
const x = 10;

const halve = function () {
  const divided = x / 2;

  const print = function () {
    console.log(x); // 10
    console.log(divided); // 5
  };

  print();
};

halve();
```

The `print` function inside halve can interact with both the `x` variable in the global scope, as well as the `divided` variable within the scope of the `halve` function. This is also known as **lexical scoping**, where each local scope can also see all the local scopes that contain it. On top of that, all scopes can see the global scope.

### Declaring Functions

We've seen functions declared as an expression. We can also assign them in a shorter way through what is known as **function declarations**.

```js
function booDeclare(name) {
  console.log(`BOO! Did I scare you ${name}?`);
}

// how we would write it before
const boo = function (name) {
  // ...
};
```

There is a difference between the two, and it's primarily due to something called _hoisting_ (we won't get into this right now). If you were to call `booDeclare` before it was declared, you would see that it still works. However, we can't say the same for the other function. This is due to function declarations being _hoisted_ up to the top of the conceptual page, and thus is able to be used anywhere in the code.

This kind of makes sense, as the second function is more like how we declare a variable, and that we are unable to know what the variable binds to before it is declared.

```js
console.log("I am walking through a haunted house", booDeclare("Alex")); // works
function booDeclare(name) {
  return `BOO! Did I scare you ${name}?`;
}

console.log(boo); // Cannot access 'boo' before initialization
const boo = function (name) {
  return `BOO! Did I scare you ${name}?`;
};

console.log(ghost); // Cannot access 'ghost' before initialization

const ghost = "nice ghost";
```

### Arrow functions

You might be familiar with arrow functions as well. They are newer syntax, and they provide us a way of writing small function expressions in a (my opinion) cleaner manner.

```js
const owedMoney = (sum) => {
  return sum;
};
// can be written as
const owedMoney = (sum) => sum;
```

The code is less verbose, as it now implicitly returns the value that sum is bound to, and there are no curly braces. There is another difference between the arrow function and a function expression, and that is regarding the keyword [this](). We will talk about it more once we get to Chapter 6 (can't wait).

### Optional Arguments

The beauty of JavaScript is that it's quite lenient in what you can do (compared to other languages).

```js
function ages() {
  console.log("I have no args");
}

ages(11, 23, 52, 59, 32, 53, 99, 29, 48, 75, 49, 23); // I have no args
```

No errors! What happens here is that JavaScript will ignore all these arguments if they're not being used. Simple. Even if you specified the arguments, and didn't provide any parameters, JavaScript will still not error out.

```js
function ages(person1, person2, person3) {
  console.log(person1, person2, person3);
}
ages(19); // 19 undefined undefined
```

JavaScript assigns missing parameters to `undefined` (similar to when you declare `let x;`). It also dismisses any parameters provided if there's no explicit use for them. As you can tell, this is not so beautiful. The downside here is that you can accidentally pass the wrong number of arguments, or none at all, and you might not realise that you have a bug.

One way to assign a value to an argument even when it's not passed is to use optional arguments.

```js
function ages(person1 = 23, person2 = 99) {
  console.log(person1, person2);
}
ages(22); // 22 99
```

Again, this is not the ultimate solution as it will only assign the parameters in order. So if you don't pass anything in the second argument, `person2` will always default to 99. That's why it's common to see code like this (albeit this is very contrived).

```js
function fetchPosts(url, method = "GET") {
  const data = fetch(url, { method });
}
```

### Functions and Side Effects

As we've seen, functions can be split into two types. Functions that execute other functions or side effects, and functions that have return values. At times, you will have functions that do both. Each have their own use cases, and their own advantages. Functions with return values will almost always be called more often, since we rely on the values returned to execute more code.

There are `pure` functions, that have the pleasure of always being reliable. The _purity_ comes from:

- relying on global variables whose values might change
- always returning/producing the same value
- can easily be replaced with a simple value:

```js
const return5 = () => 5;
let total = 5 + return5(); // 10
total = 5 + 5; // 10
```

They are easily testable, making unit tests a breeze to write. They usually are quick to understand, as you don't need to scour other parts of the codebase to see what's being called. In essence, they're great. Yet, that ease comes with a bit of difficulty. Whilst you can write primarily pure functions, you'll realise quickly that some side effects are needed. So unless you're a total purist who despises side effects, I'd say it's fine to have a mixture of both. Like the author says:

<aside>
  "There'd be no way to write a pure version of console.log, for example, and console.log is good to have."
</aside>

### Summary

So, functions. A brilliant addition to our JavaScript tool-belt that allows us to manage multiple kinds of scope, separating code logic, not repeating ourselves, and understanding side effects. The chapter gave us a lot of information, and I think it's an important fundamental to really grasp. The author also brings up concepts like the Call Stack, and Recursion. I decided not to include that in this chapter as I felt it deserved a separate `snack-esque` post. You can read more about it on my website, although the blog post is still "growing."

Thanks for reading! The next chapter will be about some rather essential data structures, [Objects and Arrays](https://eloquentjavascript.net/04_data.html).

If you'd like to attempt the exercises for the chapter, you can find them [at the bottom of the chapter](https://eloquentjavascript.net/03_functions.html#h_TcUD2vzyMe). Let me know how you get on. I definitely recommend going through them, to help solidify your knowledge.