Skip to content

Commit

Permalink
Update closures lesson to be more concise with explanations. Also fix…
Browse files Browse the repository at this point in the history
… typos
  • Loading branch information
Kalikoze committed May 23, 2024
1 parent 2618d22 commit 0e7a463
Showing 1 changed file with 17 additions and 23 deletions.
40 changes: 17 additions & 23 deletions lessons/module-2/closures.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,13 @@ module: 2

## Some Context

Closures are an important concept in programming that can help developers to create more modular and reusable code. Typically variables are declared within a specific scope, such as a function, and are then used only within that scope and any nested scopes. Closures, on the other hand, allow a function to access and manipulate variables that are declared outside of its own scope.
Closures are an important concept in programming, helping create modular and reusable code. Variables are typically declared within a specific scope, such as a function, and are used only within that scope and any nested scopes. Closures allow a function to access and manipulate variables outside its own scope.

In addition to creating more efficient and maintainable code, closures can also improve security by preventing unintended access to sensitive data. They are particularly useful in functional programming, where functions are treated as first-class citizens and can be passed as arguments or returned as values. Understanding how scope, more specifically **lexical scope**, and closures work can help design more effective and flexible software solutions.
Closures can also improve security by preventing unintended access to sensitive data. They are particularly useful in functional programming, where functions are treated as first-class citizens. Understanding scope, especially **lexical scope**, and closures can help design more effective software solutions.

## Lexical Scope

Though we haven't put a name to it until now, our conversations around scope and the scope chain are directly related to what we call **lexical scoping**.

Lexical scoping means that our scope is determined by its location within the code at the time of its declaration, meaning the scope is determined by its surrounding block structure. As a result, our code is more predictable and reliable in behavior because the scope is fixed at the time of writing and does not change during execution. Examples of lexical scope include variables like `let` and `const`, although not `var`. `var` is `statically` scoped, meaning the value is determined during the `creation-phase` (*also known as build time*), where `hoisting` also happens. You can see lexical scope by looking directly at code below:
**Lexical scoping** means that the scope of a variable is determined by its location within the code at the time of its declaration. The scope is defined by the surrounding block structure. This makes the code more predictable and reliable because the scope is fixed at the time of writing and does not change during execution.

```js
function eatSnack() {
Expand All @@ -34,7 +32,7 @@ function eatSnack() {
getHangry();

function getHangry() {
console.log('I am sooooooo HANGRY!');
console.log(`I am sooooooo HANGRY! My hunger level is ${hunger}.`);
}
}

Expand All @@ -44,19 +42,15 @@ eatSnack();
<section class="answer">
### Explaining It Further

In our example above, the lexical scope for our `getHangry` function is the scope (and any variables) that are contained within `eatSnack`.
In the example above, the lexical scope of the `getHangry` function includes the scope and variables within `eatSnack`.

All inner functions (since functions create new scope) are statically (lexically) bound during the Creation Phase to the parent context in which the inner function was physically defined in the source/program code. Said another way, the `getHangry()` function is defined inside the definition of the `eatSnack()` function - because of this, we can't call `getHangry()` outside of the `eatSnack()` function due to its scope being limited within.
Inner functions are bound to their parent context during the Creation Phase, based on where they are defined in the source code. Since `getHangry` is defined inside `eatSnack`, it cannot be called outside of `eatSnack` due to its limited scope.
</section>


## Closures

[Closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures) are expressions (usually functions) which can work with variables set within a certain context. In other words, a closure is formed when a function is defined inside of another function (one function nested inside of another function). This allows the inner function to access the outer function's variables via the scope chain.

Let's build on a definition together. At the most basic level, **a closure is when an inner function is defined inside of another function**.

This is syntactically and factually correct, but it doesn't tell us much about what's significant here. So a more thorough definition might be: **a closure is when an inner function has access to its outer function's variables**.
[Closures](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures){:target="\__blank"} are functions that capture and remember their lexical environment. Another way you can describe it is **a closure is when a nested function can access variables from its outer function**.

<section class="call-to-action">
### Closure Example
Expand Down Expand Up @@ -110,7 +104,7 @@ function greet() {
var createGreeting = greet();
```

- What is the value of `createGreeting`? Are you able to acess the `displayName` function from here?
- What is the value of `createGreeting`? Are you able to access the `displayName` function from here?
</section>

<section class="answer">
Expand All @@ -124,19 +118,19 @@ In JavaScript this isn't the case. Because there is an inner function here `disp
<section class="note">
### "What is a closure?"

This is why the question, "What is a closure?", has become such a big question in JavaScript interviews. Often people coming from other languages are really surprised by this different behavior since normaly the `firstName` variable would not be able to be reference.
This is why the question, "What is a closure?", has become such a big question in JavaScript interviews. Often people coming from other languages are really surprised by this different behavior since normally the `firstName` variable would not be able to be referenced.

So our newer, better definition of a closure could now be: **When an inner function has access to the outer function's variables and can remember the environment in which it was created.** (*So not only does our inner function have access to the outer function's variables, but it also remembers their values, even if the outer function has finished executing!*)
</section>

## Closures for Protecting Variables

This still might not seem like an incredibly useful feature of JavaScript, but there are some more practical use-cases for closures. Before we move on, What we want to point out about this example, in order to better understand closures, is that:
While closures might not seem immediately useful, they have practical applications. Some key points to understand from the previous example is:

* `firstName` is not available anywhere outside of the `greet` function. We would say that the `greet` function "closes over" this variable
* the inner function, `displayName` can reference (or even modify) the `firstName` variable because it is inside the scope of the outer function `greet`
* `firstName` is only accessible within the `greet` function, meaning `greet` "closes over" this variable.
* The inner function `displayName` can access (or modify) `firstName` because it is within the scope of `greet`.

So one use-case for closures is to **protect variables from any sort of outside manipulation (we're not talking about security here)**. Other languages often have some really nice way of implementing private or protected variables, but JavaScript doesn't. So if this is something we want to achieve, we would use a closure.
One practical use of closures is to **protect variables from external manipulation**. Unlike other languages with built-in private or protected variables, JavaScript uses closures to achieve this.


<section class="call-to-action">
Expand Down Expand Up @@ -179,7 +173,7 @@ console.log(instructor.viewGrades());

Our most thorough definition of a closure is now **when an inner function has access to the outer function's variables and can remember the environment in which it was created. The outer function's variables are protected by the closure and can only be manipulated by code defined within that function.**

In the previous example, you'll notice we could still technically change those grades and snoop on them if we wanted to. This is why we say JavaScript doesn't have a true fashion for creating private variables. As developers, we can hide a variable declaration inside a function in order to imply that no one should be fussing with that variable - but we can still gain access to that value. So closures aren't really going to help if you have truly sensitive data that nobody should be able to see.
However, closures don't provide true privacy. While they can hide variables, those variables can still be accessed if someone really tries. Thus, closures aren't suitable for protecting truly sensitive data.
</section>


Expand All @@ -202,13 +196,13 @@ console.log(rent.decRent(1000));
console.log(rent.incRent(2000));
console.log(rent.getRent()); // returns 9500
```
***Hint:*** `rentPrice` should return a series of functions for updating our confidential rent. Take note of the dot notation used to invoked these private methods.
***Hint:*** `rentPrice` should return a series of functions for updating our confidential rent. Take note of the dot notation used to invoke these private methods.

</section>

## Conclusion

Closures are functions that capture and remember the environment in which they were created, and can be used to create private variables. Implementation of closures can result in efficient, modular, and maintainable code, and avoid common pitfalls such as global namespace pollution, code duplication, and side effects. While implementation of closures is **NOT** expected in your projects moving forward, they will become even more relevant when working with React functional components in the future. They are also used in more advanced topics such as *higher order functions* and *memoization*, which you'll explore more in the future.
Closures are functions that capture and remember the environment in which they were created, allowing access to outer function variables. They enable the creation of private variables and can lead to efficient, modular, and maintainable code. While implementation of closures is **NOT** expected in your projects moving forward, they will become increasingly relevant, especially in advanced topics such as *higher-order functions* and *memoization*.

<section class="checks-for-understanding">
### Checks for Understanding
Expand All @@ -222,7 +216,7 @@ console.log(rent.getRent()); // returns 9500
<section class="call-to-action">
### A Real World Example

Next week, we'll have a follow up session to explore how a closure can be used in a larger application setting. If you're curious to explore sooner, you can clone down and explore this [IdeaBox repo](https://github.com/Kalikoze/fp-ideabox){:target="\__blank"} that implements a closure. Ths project also follows a number of other functional programming conventions including keeping functions pure.
Next week, we'll have a follow up session to explore how a closure can be used in a larger application setting. If you're curious to explore sooner, you can clone down and explore this [IdeaBox repo](https://github.com/Kalikoze/fp-ideabox){:target="\__blank"} that implements a closure. This project also follows a number of other functional programming conventions including keeping functions pure.
</section>

### Additional Resources
Expand Down

0 comments on commit 0e7a463

Please sign in to comment.