Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,24 @@ Before ECMAScript 6, JavaScript had a single keyword to declare a variable, `var
In ECMAScript 6, the keywords `let` and `const` were introduced. While `var` variables were function scoped, these allow you to define variables that are **block scoped** - basically, scoping the variable to only be available within the closest set of `{ curly braces }` in which it was defined. These braces can be those of a `for` loop, `if-else` condition, or any other similar construct, and are called, a block. Let's see an example to sum this all up.

```javascript
let globalAge = 23; // This is a global variable
// This is a global variable
let globalAge = 23;

// This is a function - and hey, a curly brace indicating a block
function printAge (age) {
var varAge = 34; // This is a function scoped variable
function printAge(age) {
// This is a function scoped variable
var varAge = 34;

// This is yet another curly brace, and thus a block
if (age > 0) {
// This is a block-scoped variable that exists
// within its nearest enclosing block, the if's block
// within its nearest enclosing block: the if's block
const constAge = age * 2;
console.log(constAge);
}

// ERROR! We tried to access a block scoped variable
// not within its scope
// outside its scope
console.log(constAge);
}

Expand All @@ -54,35 +56,43 @@ Take a while to brew on that example. In the end, it's not some mind-blowing con

### Closures aren't scary

You know how if you needed to repeatedly needed to get or create some value based on some kind of input, you'd use a function with parameters? For example, you could write a function that takes arguments and returns an array or string or number as a result of using those arguments. Functions are no different. You can use a function to create another function.

The best way to approach this would be to start with an example - take a look at this piece of code below.

```javascript
function makeAdding (firstNumber) {
// "first" is scoped within the makeAdding function
const first = firstNumber;
return function resulting (secondNumber) {
// "second" is scoped within the resulting function
const second = secondNumber;
return first + second;
function makeAddingFunction(firstNumber) {
// firstNumber is scoped anywhere within makeAddingFunction,
// including returnedFunction
// any variables declared here will also be accessible within returnedFunction

// we don't need to name the returned function
// this is just to reference more easily in explanation
return function returnedFunction(secondNumber) {
// secondNumber is scoped only within returnedFunction
return firstNumber + secondNumber;
}
}
// but we've not seen an example of a "function"
// being returned, thus far - how do we use it?

const add5 = makeAdding(5);
console.log(add5(2)) // logs 7
```

A lot going on, so let's break it down:
We can then create our function by calling `makeAddingFunction`:

```javascript
const add5 = makeAddingFunction(5);
console.log(add5(2)); // 7

const add8 = makeAddingFunction(8);
console.log(add8(2)); // 10

1. The `makeAdding` function takes an argument, `firstNumber`, declares a constant `first` with the value of `firstNumber`, and returns another **function**.
1. When an argument is passed to the returned function, which we have assigned to **add5**, it returns the result of adding up the number passed earlier to the number passed now (`first` to `second`).
const add79100105110 = makeAddingFunction(79100105110);
console.log(add79100105110(111687378)); // 79211792488
```

Now, while it may sound good at first glance, you may already be raising your eyebrows at the second statement. As we've learned, the `first` variable is scoped within the `makeAdding` function. When we declare and use `add5`, however, we're **outside** the `makeAdding` function. How does the `first` variable still exist, ready to be added when we pass an argument to the `add5` function? This is where we encounter the concept of closures.
Instead of writing a new function every time, we just use a function to create a function for us - the same way we might write a `toFormattedDateString(date)` function and call it several times with different dates, rather than hardcoding the logic and resulting string every single time.

Functions in JavaScript form closures. A closure refers to the combination of a function and the **surrounding state** in which the function was declared. This surrounding state, also called its **lexical environment**, consists of any local variables that were in scope at the time the closure was made. Here, `add5` is a reference to the `resulting` function, created when the `makeAdding` function is executed, thus it has access to the lexical environment of the `resulting` function, which contains the `first` variable, making it available for use.
`returnedFunction` forms a closure around the `firstNumber` parameter. A closure refers to the combination of a function and the **surrounding state** in which the function was declared. This surrounding state, also called its **lexical environment**, consists of any local variables that were in scope at the time the closure was made. Here, `add5` is a reference to the function returned by the `makeAddingFunction(5)` call. After `makeAddingFunction(5)` finishes executing, the 5 is not cleaned up in memory because the returned function still needs it: the returned function has access its lexical environment (which in this case, contains the `firstNumber` parameter).

This is a **crucial** behavior of functions - allowing us to associate data with functions and manipulate that data anywhere outside of the enclosing function. If you're still confused, read the [MDN documentation on Closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures), but only the sections "Lexical scoping", "Closure" and "Practical closures". The other sections refer to concepts that will be discussed later in this lesson.
This is a **crucial** behavior of functions; it allows us to associate data with functions and manipulate that data anywhere outside of the enclosing function. We can make use of this in factory functions, coming up shortly.

### So, what's wrong with constructors?

Expand All @@ -99,21 +109,23 @@ Because of that, constructors have become unpopular in favor of a pattern that i
These fancy-sounding functions work very similar to how constructors did, but with one key difference - they levy the power of closures. Instead of using the `new` keyword to create an object, factory functions set up and return the new object when you call the function. They do not use the prototype, which incurs a performance penalty - but as a general rule, this penalty isn’t significant unless you’re creating thousands of objects. Let's take a basic example to compare them to constructor functions.

```javascript
const User = function (name) {
function User(name) {
this.name = name;
this.discordName = "@" + name;
}
// hey, this is a constructor -
// then this can be refactored into a factory!
```

Hey, this is a constructor... then this can be refactored into a factory!

function createUser (name) {
```javascript
function createUser(name) {
const discordName = "@" + name;
return { name, discordName };
}
// and that's very similar, except since it's just a function,
// we don't need a new keyword
```

This is all very similar to the constructor, except this is just a normal function, meaning we do not need to call it with the `new` keyword.

<div class="lesson-note" markdown="1">

#### The object shorthand notation
Expand All @@ -134,36 +146,43 @@ However, now, if we have a variable with the same name as that of the property t
const nowFancyObject = { name, age, color };
```

An added advantage to this is that it's now possible to console.log values neatly!
An added advantage to this is that it's now possible to console.log values neatly! If you wanted to log the name, age and color variables together earlier, you would have done something like:

```javascript
// If you wanted to log these values, earlier,
// you would have done the following
console.log(name, age, color);
// which would have resulted in a mess - Bob 28 red
```

This would log something like `Bob 28 red`. Not *bad*, just not the clearest as to what's what. Instead, try wrapping it in some curly braces now, which makes it an object:

// Try wrapping it in some { curly braces } now,
// which makes it an object!
```javascript
// shorthand for console.log({ name: name, age: age, color: color })
console.log({ name, age, color });
// now it logs as - { name: "Bob", age: 28, color: "red" }
```

Now it'll log as `{ name: "Bob", age: 28, color: "red" }` which is much clearer, and we didn't need to manually add labels!

### Destructuring

Yet another expression allows you to "unpack" or "extract" values from an object (or array). This is known as **destructuring**. When you have an object, you can extract a property of an object into a variable of the same name, or any named variable for an array. Take a look at the example below:

```javascript
const obj = { a: 1, b: 2 };
const { a, b } = obj;
// This creates two variables, a and b,
// which are equivalent to

// equivalent of doing
// const a = obj.a;
// const b = obj.b;
const { a, b } = obj;
```

And with arrays:

```javascript
const array = [1, 2, 3, 4, 5];
const [ zerothEle, firstEle ] = array;
// This creates zerothEle and firstEle, both of which point
// to the elements in the 0th and 1st indices of the array

// equivalent of doing
// const zerothEle = array[0];
// const firstEle = array[1];
const [zerothEle, firstEle] = array;
```

The [MDN documentation on destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) has some great examples and should be a good read for this concept.
Expand All @@ -175,12 +194,12 @@ The [MDN documentation on destructuring assignment](https://developer.mozilla.or
Now you may be thinking - where does closure come into all of this? Factories seem to be returning an object. This is where we can extend our `User` factory to add a few more variables and introduce "private" ones. Take a look at this, now:

```javascript
function createUser (name) {
function createUser(name) {
const discordName = "@" + name;

let reputation = 0;
const getReputation = () => reputation;
const giveReputation = () => reputation++;
const giveReputation = () => { reputation++; };

return { name, discordName, getReputation, giveReputation };
}
Expand All @@ -189,11 +208,11 @@ const josh = createUser("josh");
josh.giveReputation();
josh.giveReputation();

// logs { discordName: "@josh", reputation: 2 }
console.log({
discordName: josh.discordName,
reputation: josh.getReputation()
});
// logs { discordName: "@josh", reputation: 2 }
```

We’ve introduced a new metric for a new user - a reputation. Notice that the object we return in the factory function does not contain the `reputation` variable itself, nor any copy of its value. Instead, the returned object contains two functions - one that reads the value of the `reputation` variable, and another that increases its value by one. The `reputation` variable is what we call a "private" variable, since we cannot access the variable directly in the object instance - it can only be accessed via the closures we defined.
Expand All @@ -215,43 +234,51 @@ Note that you could technically also use closure in constructors, by defining th
In the lesson with constructors, we looked deeply into the concept of prototype and inheritance, and how to give our objects access to the properties of another. With factory functions too, there are easy ways to do that. Take another hypothetical scenario into consideration. We need to extend the `User` factory into a `Player` factory that needs to control some more metrics - there are some ways to do that:

```javascript
function createPlayer (name, level) {
function createPlayer(name, level) {
const { getReputation, giveReputation } = createUser(name);

const increaseLevel = () => level++;
const increaseLevel = () => { level++; };
return { name, getReputation, giveReputation, increaseLevel };
}
```

And there you go! You can create your User, extract what you need from it, and re-return whatever you want to - hiding the rest as some private variables or functions! In case you want to extend it, you can also use the [`Object.assign` method](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign) to add on the properties you want!

```javascript
function createPlayer (name, level) {
function createPlayer(name, level) {
const user = createUser(name);

const increaseLevel = () => level++;
const increaseLevel = () => { level++; };
return Object.assign({}, user, { increaseLevel });
}
```

### The module pattern: IIFEs

<div class="lesson-note lesson-note--warning" markdown="1" >
<div class="lesson-note lesson-note--warning" markdown="1">

#### ECMAScript 6 modules
#### ES6 modules

ECMAScript 6 introduced a new JavaScript feature called "modules" - which are a set of syntax for importing and exporting code between different JavaScript files. While they are important and powerful, they are covered a bit later in the curriculum. We are not talking about them in this section.
ECMAScript 6 (released in 2015) introduced a new JavaScript feature called "modules", which are a set of syntax for importing and exporting code between different JavaScript files. For now, we will be talking more generally about the module pattern using IIFEs, which you will still see out in the wild. In a later lesson, we will cover using ES6 modules for similar purposes.

</div>

Oftentimes, you do not need a factory to produce multiple objects - instead, you are using it to wrap sections of code together, hiding the variables and functions that you do not need elsewhere as private. This is easily achievable by wrapping your factory function in parentheses and immediately calling (invoking) it. This immediate function call is commonly referred to as an Immediately Invoked Function Expression (duh) or IIFE in short. This pattern of wrapping a factory function inside an IIFE is called the module pattern.
Oftentimes, you do not need a factory to produce multiple objects - instead, you are using it to wrap sections of code together, hiding the variables and functions that you do not need elsewhere as private. This is easily achievable by wrapping your factory function in parentheses and immediately calling (invoking) it. This immediate function call is commonly referred to as an Immediately Invoked Function Expression (duh) or IIFE in short. IIFEs are quite literally just function expressions that are called immediately:

```javascript
// This is an IIFE! Though not particularly useful, of course.
(() => console.log('foo'))();
```

A more helpful use of IIFEs is the pattern of wrapping "private" code inside an IIFE: the module pattern. This is often done with factory functions:

```javascript
const calculator = (function () {
const add = (a, b) => a + b;
const sub = (a, b) => a - b;
const mul = (a, b) => a * b;
const div = (a, b) => a / b;

return { add, sub, mul, div };
})();

Expand All @@ -270,6 +297,10 @@ This is where we encounter the word **encapsulation** - bundling data, code, or

Take the calculator example into consideration. It's very easy to imagine a scenario where you can accidentally create multiple functions with the name `add`. What does `add` do - does it add two numbers? Strings? Does it take its input directly from the DOM and display the result? What would you name the functions that do these things? Instead, we can easily encapsulate them inside a module called `calculator` which generates an object with that name, allowing us to explicitly call `calculator.add(a, b)` or `calculator.sub(a, b)`.

#### Why the IIFE?

But then why not just write the factory function then call it once? Why bother with the IIFE? Well without the IIFE, we'd have to give the function a name so we can call it afterwards. Now it has a name, it's both taken a name up in that scope and also means it's reusable in that scope. That may not be desirable - we may purposely want to invoke it only once. By wrapping the factory in an IIFE, we achieve the same code flow, except we no longer need to name the function, which also means it can't be referenced later. We are packing the code that creates a calculator into what's effectively a module, then exposing only what needs to be used later in the code: the `calculator` object.

### Assignment

<div class="lesson-content__panel" markdown="1">
Expand All @@ -278,6 +309,7 @@ Take the calculator example into consideration. It's very easy to imagine a scen
- [The article on scope](https://wesbos.com/javascript/03-the-tricky-bits/scope)
- [The article on closures](https://wesbos.com/javascript/03-the-tricky-bits/closures)
1. Read this article on [module pattern in JavaScript](https://dev.to/tomekbuszewski/module-pattern-in-javascript-56jm) by Tomek Buszewski.
1. Read [MDN's guide on closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures).

</div>

Expand All @@ -286,7 +318,7 @@ Take the calculator example into consideration. It's very easy to imagine a scen
The following questions are an opportunity to reflect on key topics in this lesson. If you can't answer a question, click on it to review the material, but keep in mind you are not expected to memorize or master this knowledge.

- [How does scope work in JavaScript?](#scoopfuls-of-scopes)
- [What are closures and how do they help in creating private variables?](#closures-arent-scary)
- [What are closures?](#closures-arent-scary)
- [What common issues can you face when working with constructors?](#so-whats-wrong-with-constructors)
- [What are private variables in factory functions and how can they be useful?](#private-variables-and-functions)
- [How can we implement prototypal inheritance with factory functions?](#prototypal-inheritance-with-factories)
Expand Down