More Closure Examples | JavaScript 🔥 | Lecture 129

The Coding Classroom
29 Apr 202315:31

Summary

TLDRThis video tutorial explores the concept of closures in JavaScript, offering two illustrative examples to help viewers recognize closures in their own code. The first example demonstrates how a closure enables a function to remember and access variables from its lexical scope, even after that scope has exited. The second example uses a timer to show that closures allow callback functions to access variables from the enclosing function's scope, even after it has executed. These examples clarify that closures form without needing to return a function from another function, showcasing their ability to maintain access to the birthplace variables, thus deepening the understanding of closures and their practical applications in coding.

Takeaways

  • 🖥 Closures do not require returning a function from another function; they can occur in various situations.
  • 📖 The example with variables 'f' and 'g' demonstrates that a closure can capture and remember the environment in which a function was created, allowing access to local variables even after their parent function has executed.
  • 📈 Reassigning a function to a variable (like 'f' in the examples) shows how closures can update to encapsulate new environments, demonstrating the dynamic nature of closures.
  • 🤖 Closures ensure that functions retain access to the variables of their originating scope, effectively 'remembering' the values at the time they were defined.
  • 🚡 The analogy of a variable being inside the 'backpack' of a function helps illustrate how closures carry their scope with them.
  • 🛠 Modifying the 'f' variable with different functions (as shown with functions 'g' and 'h') highlights how closures adapt to the current scope, changing the variables they enclose.
  • 📚 The timer example with 'board passengers' function illustrates closures in asynchronous operations, where functions can access variables from their creation scope even if the parent function has completed.
  • 💻 Closures have precedence over the scope chain, meaning a function will use variables from its closure before considering the global scope.
  • 🏆 Identifying closures in your code is crucial for understanding behavior, especially in complex scenarios involving callbacks, timers, and asynchronous execution.
  • 🔧 The script underscores the importance of closures in JavaScript, showing their versatility and critical role in function scope and execution context management.

Q & A

  • What is a closure in JavaScript?

    -A closure in JavaScript is a feature where an inner function has access to the variables of its outer enclosing function, even after the outer function has executed.

  • Can closures be created without returning a function from another function?

    -Yes, closures can be created without returning a function from another function, as demonstrated in the examples where functions manipulate external variables.

  • How was the closure demonstrated in the first example with the variable 'f'?

    -In the first example, a closure was demonstrated by defining a function 'g' that assigns a new function to the variable 'f', which accesses and modifies an external variable 'a'. Calling 'f' later shows it retains access to 'a', demonstrating closure.

  • What happens to the closure when the variable 'f' is reassigned to a new function in the examples?

    -When 'f' is reassigned to a new function, the closure changes to enclose the variables of the new assignment context, demonstrating that a closure always maintains a connection to the variables present at its 'birthplace'.

  • How does the second example involving a timer demonstrate a closure?

    -The second example demonstrates a closure with a timer by showing that a callback function used in 'setTimeout' retains access to the variables of its enclosing function ('board passengers'), even after the enclosing function has executed.

  • Why does the callback function in the timer example still have access to 'n' and 'perGroup' variables?

    -The callback function retains access to 'n' and 'perGroup' due to closure, which allows it to 'remember' and access variables from its enclosing function's scope, even after the function has executed.

  • What proves that closures have priority over the scope chain?

    -The fact that the callback function in the timer example uses the 'perGroup' variable from its enclosing function's scope, instead of a global 'perGroup' variable, proves that closures have priority over the scope chain.

  • How is a closure's ability to 'remember' variables from its birthplace significant in JavaScript?

    -A closure's ability to 'remember' variables from its birthplace is significant because it ensures that a function does not lose connection to its defining environment's variables, allowing for powerful programming patterns like encapsulation and factory functions.

  • What is the impact of reassigning a function to the variable 'f' on its closure?

    -Reassigning a function to the variable 'f' updates its closure to enclose variables from the new assignment's context, illustrating how closures adapt to reflect the current scope of a function.

  • How does the concept of closures enhance the identification of them in code?

    -Understanding closures helps in identifying them in code by recognizing patterns where functions access and manipulate variables from an outer scope, even after the outer function has executed, enabling more effective debugging and code optimization.

Outlines

00:00

🧩 Exploring Closures Through Examples

The segment introduces closures using two examples, emphasizing that closures can be created without returning a function from another function. The first example demonstrates how a function, assigned to a variable outside its scope, can retain access to a variable within its defining scope, even after that scope has ended. This is shown through the reassignment of the variable 'f' to a new function that still has access to the variable 'a' after the execution context of 'g' has finished. The second part of the example further explores closures by reassigning 'f' to another function within a different scope, demonstrating how 'f' can close over new variables ('b') from the new scope, showing the dynamic nature of closures.

05:01

🔍 Analyzing Closure Behavior in Depth

This paragraph delves deeper into the behavior of closures, particularly focusing on the transition of the closure from enclosing one variable to another as functions are reassigned. It highlights how closures ensure functions maintain access to the variables present at their creation, regardless of reassignments. The example used demonstrates that upon reassignment, the function 'f' changes its closure from enclosing variable 'a' to enclosing variable 'b', effectively losing access to 'a'. This transition illustrates the closure's ability to adapt to the function's current environment, ensuring it always remembers the variables of its 'birthplace.'

10:02

⏱ Demonstrating Closures with Timers

This section introduces a practical example of closures involving timers, illustrating how closures allow callback functions to access variables from their defining scope, even after that scope has ended. Using a 'board passengers' function that divides passengers into groups and sets a timer for boarding, the example shows that the callback function retains access to the 'n' (number of passengers) and 'perGroup' variables despite being executed after the 'board passengers' function has completed. This example underscores the closure's role in preserving the connection to its original variable environment, allowing access to variables defined in the parent function.

15:04

📘 Closing Thoughts on Closures

The final paragraph reflects on the preceding discussions about closures, aiming to enhance the viewer's ability to identify closures in their own or others' code. It positions closures as an essential concept in programming that will continue to appear in future coding challenges and discussions. The speaker encourages the viewers to think critically about closures, preparing them for an upcoming coding challenge that further explores this concept.

Mindmap

Keywords

💡Closures

Closures are a fundamental concept in programming where a function retains access to its lexical scope even when the function is executed outside that scope. In the context of the video, closures are illustrated through examples showing how functions can access variables from their defining scope, even after that scope has been exited. This is exemplified when functions `F` and a new function within `H` retain access to variables `a` and `b`, respectively, demonstrating closure behavior.

💡Function Expression

A function expression involves defining a function within an expression, such as assigning it to a variable. The video discusses creating a function expression `g` and assigning another function to variable `F`. This demonstrates how function expressions can be utilized to dynamically assign and redefine functions in JavaScript, leading to closures.

💡Lexical Scope

Lexical scope refers to the region in code where a variable is defined and accessible. The video emphasizes how closures are linked to lexical scope, showing that functions remember the scope in which they were defined, allowing them to access variables from that scope even after the scope execution has finished, as shown with `F` accessing variable `a` and the second function accessing `b`.

💡Variable Reassignment

Variable reassignment occurs when a new value is assigned to an already defined variable. The script highlights reassigning the variable `F` to different functions to demonstrate closures. It shows that despite reassignment, each function retains access to the specific variables within its lexical scope at the time of definition, such as `a` and `b`.

💡Global Scope

Global scope is the outermost layer of variable accessibility, where variables are accessible from anywhere in the code. The video starts by defining `F` in the global scope, showing how variables and functions defined in this scope can be accessed and modified throughout the code, facilitating the demonstration of closures with functions defined within other functions.

💡Execution Context

The execution context is the environment in which a function executes, including all the variables accessible at that time. The video uses this concept to explain how the function assigned to `F` retains a connection to the execution context of `g` and `h`, illustrating how closures enable functions to access variables from their defining execution context even after it has ended.

💡Callback Function

A callback function is a function passed into another function as an argument, which is then invoked inside the outer function to complete some kind of routine or action. The video uses a timer as an example, where a callback function is executed after a delay, demonstrating closures by accessing the `n` and `perGroup` variables from the `boardPassengers` function's scope.

💡setTimeout

The `setTimeout` function is a built-in JavaScript function that executes a specified piece of code or function after a specified delay. In the video, `setTimeout` is used in the timer example to illustrate closures, showing how the callback function can access variables from the `boardPassengers` function even after a delay, highlighting the non-blocking nature of JavaScript.

💡Scope Chain

The scope chain in JavaScript is a hierarchy of scopes, where each function has access to variables in its own scope as well as those in outer scopes. The video explores how closures can override the scope chain, allowing functions to access variables from their original defining scope rather than just the variables available in the current execution context or global scope.

💡Variable Environment

The variable environment refers to the specific set of variable bindings available in a function's execution context. The video delves into how closures involve functions 'remembering' the variable environment of their defining scope. This is evidenced by the inspection of `F`'s variable environment, showing how it changes with reassignments and retains access to relevant variables (`a` or `b`) depending on its definition context.

Highlights

We don't need to return a function from another function in order to create a closure.

A closure is created when a function closes over variables from its outer scope, even when those variables are not defined within the function itself.

A closure allows a function to access variables from its outer scope, even after that outer scope's execution has completed.

The closure contains the variables that were present at the function's 'birthplace' or the scope in which it was defined.

When a function is reassigned to a new value, its closure changes to include the variables from the new 'birthplace'.

A timer is a good example of a closure being created without returning a function from another function.

The setTimeout function takes a callback function as an argument, which is executed after a specified delay.

The callback function passed to setTimeout has access to variables from the scope in which it was created, even after that outer scope has finished executing.

The closure created by the callback function has priority over the scope chain, allowing it to access variables from its outer scope rather than global variables with the same name.

Identifying closures in your own code is an important skill to develop.

The examples demonstrate that closures can be created without returning a function from another function.

The first example shows how a function can close over variables from its outer scope, even when it is assigned to a variable outside that scope.

The second example illustrates how a closure allows a callback function to access variables from its outer scope, even after that outer scope has finished executing.

The closure created by the callback function includes the arguments passed to the outer function, as arguments are treated as local variables.

The coding challenge at the end of the video will provide an opportunity to practice identifying closures in code.

Transcripts

play00:01

Let's now create two more situations

play00:04

in which closures are gonna appear.

play00:06

So that you can start identifying closures

play00:10

in your own code in the future.

play00:13

And both of these examples are gonna demonstrate

play00:16

that we don't need to return if function

play00:19

from another function in order to create a closure.

play00:23

All right, so let's start with our first example here.

play00:28

I'm gonna start by defining an empty variable called f

play00:34

and then a function expression g.

play00:42

So these are just some generic names

play00:45

because what the functions do here don't really matter.

play00:50

Then in here, I defined this a variable

play00:53

and then I'm going to reassign the F variable

play00:57

that we created out here.

play00:59

And I'm gonna assign it a function value.

play01:04

And this function will then simply log

play01:07

the a variable to the console

play01:10

let's say times two.

play01:12

Okay, and that's it for now at least.

play01:17

So let's try to call g

play01:22

so that's gonna be dispersed function.

play01:24

And so the result of this function is that a will be 23

play01:29

and that F variable that we have out here

play01:32

will become this function.

play01:34

And so after g we can then call F.

play01:39

So let's try that.

play01:41

And we get 46 which is indeed 23 times two, all right.

play01:48

And so what this proves is that this F value here

play01:53

so this F function really does close over any variables

play01:57

of the execution context in which it was defined.

play02:01

And that is true even when the variable itself.

play02:06

So F here was technically not even defined inside

play02:11

of this variable environment here, right.

play02:14

So this F variable was defined outside here

play02:17

in the global scope, it was created here

play02:20

but then as we assigned it a function here

play02:23

in this the g function,

play02:25

so right here at instill closed

play02:27

over the variable environment of the g function.

play02:31

And that includes this a variable.

play02:34

And therefore it is able to access this a variable here

play02:38

even after the g function here at this point

play02:41

has of course already finished its execution, right.

play02:46

So that's just what we learned in the last lecture.

play02:49

So at this point of the execution,

play02:51

the variable environment of g is no longer there, right.

play02:56

But f, so this function here closed over

play02:59

that variable environment and therefore it is able

play03:03

to access the a variable.

play03:08

So basically using the analogy of or less video,

play03:11

the a variable is inside the backpack of the f function.

play03:17

But now let's take it to the next level

play03:20

and create a new function here.

play03:25

So that's h and it's gonna be very similar.

play03:29

All it's gonna do is to define b as some other value,

play03:35

let's say this and then we will again,

play03:39

reassign f, right here.

play03:43

So let's call this one a b now of course.

play03:46

So this one here will then try to access b

play03:49

and multiply by two.

play03:52

Okay, and so what I want to see with this

play03:56

is what happens when we assign the f value

play03:59

a second function.

play04:01

Okay, so here we call g and so g will then assign to

play04:06

or empty variable this f function.

play04:09

Okay, and then if we call h afterwards

play04:14

or actually let's do it after calling f for the first time.

play04:18

So as we then call h then the f variable

play04:21

will be assigned again.

play04:23

So then a second function which is this one here.

play04:27

And so what I want to see is what f does then.

play04:31

So let's see.

play04:33

And again, keeping in mind that F at this point

play04:36

is a different function than this one here

play04:39

because it was reassigned by h.

play04:43

We just write that here.

play04:51

Okay, and now we get 1554 which is probably 777 times two

play05:00

and indeed it is.

play05:02

And so this proves that the f function

play05:05

it was reassigned here

play05:07

also closed over the variable environment of h.

play05:11

And so that's how it can access then the b variable here

play05:15

which has set to 777.

play05:18

So let's actually inspect the variable environment like

play05:22

we did by the end of the last video.

play05:25

So of f and then we will be able to see that in the closure,

play05:34

it does indeed have the value of b

play05:37

and it now no longer has the value of a.

play05:40

So after the closure that it used to have before.

play05:44

So at this point here at the closure contained

play05:47

the value a and we can of course also see that.

play05:53

So at this point in time, the closure is indeed a 23, right.

play06:04

And so then as we reassign the function to a new value,

play06:09

then that old closure basically disappears

play06:12

and now the closure is b.

play06:17

So this variable here of the birthplace

play06:20

where it was reassigned.

play06:22

And this is really fascinating in my opinion

play06:25

that the closure can change like this

play06:28

as the variable is reassigned.

play06:30

So it really is true that a closure always makes sure

play06:34

that a function does not lose the connection

play06:37

to the variables that were present at its birthplace.

play06:41

So it's always gonna remember them.

play06:44

In our case here, the function

play06:45

was kind of born inside of g first

play06:49

and then it was essentially reborn again in h.

play06:54

And so first the closure contained the a variable

play06:57

of its first birthplace.

play06:59

And then as it was reborn to follow our analogy

play07:03

then it remembered this B variable off its birthplace.

play07:08

Okay, so this was example one.

play07:14

Let's now create another example.

play07:21

Okay, so I hope the first one was clear here.

play07:24

So whenever something like this happens

play07:26

where your reassigned functions even without returning them,

play07:30

keep in mind that also this will create a closure

play07:35

but now moving on to our second example here

play07:39

and that's gonna be a timer.

play07:41

So a timer is another great example

play07:43

that we don't need to return a function

play07:46

to see a closure in action.

play07:49

So here I'm going back to the example of the airline.

play07:57

So creating a function called board passengers

play08:03

and then we get the number of passengers and a wait time.

play08:10

Then let's create a new variable in here

play08:13

which I'm calling perGroup.

play08:16

And that's because boarding usually happens in groups

play08:20

and we usually have three groups.

play08:22

So I'm gonna divide all the passengers.

play08:24

So the number of passengers that is boarding by three

play08:30

and by the end of this function,

play08:31

I want to lock to the console.

play08:35

We'll start boarding and then wait seconds, okay.

play08:46

And now let's actually use a timer

play08:48

and we haven't actually used a timer yet in this course.

play08:52

And we will learn more about them later

play08:54

but this is such a good use case

play08:56

that I wanted to use a timer now.

play09:00

And it's actually very simple.

play09:01

So we just use the set time out function

play09:06

and a set time out function needs two parameters.

play09:09

The first one is a function which will be executed, okay.

play09:15

And this function will be executed after a certain time.

play09:19

For example, we can specify a 1,000 milliseconds

play09:23

because the second argument here is milliseconds.

play09:26

And so that will mean that whatever code

play09:28

is inside of this function will be executed

play09:31

after one second.

play09:34

Let me actually do that out here

play09:36

just so you see this as an example.

play09:42

So if I saved us now wait one second

play09:45

and then here you see timer appearing.

play09:47

So I'm gonna do that again.

play09:50

Okay, so you'll see that after this one second,

play09:53

this function was executed.

play09:56

Okay, and so this, as we already learned before

play09:59

is essentially a callback function.

play10:01

And in this case it is literally called later.

play10:05

So in this case, after one second.

play10:08

So let's create or callback function here.

play10:13

And first I want to log to the console.

play10:15

We are now boarding all and then the n passengers.

play10:25

And so this n here is this parameter of the function.

play10:30

So of this parent function which essentially is the function

play10:35

or the scope in which this callback function here

play10:38

is being created.

play10:40

And so that will of course be important

play10:42

for our closure example.

play10:46

And then let's also use the perGroup variable now.

play10:49

There are three groups each with

play11:00

passengers, okay,

play11:03

that's our function and now let's call it.

play11:06

And here we want to specify wait and then times 1,000

play11:12

because we're gonna pass into wait in seconds.

play11:16

And this argument here needs to be in milliseconds

play11:19

and so we just multiply by 1,000.

play11:23

So board passengers, let's say we have 180 passengers

play11:29

and it takes two, let's say three seconds.

play11:32

So immediately when we call this function,

play11:36

this variable will be created

play11:38

then set time out will be called

play11:40

and it will basically register this callback function here.

play11:44

And then that will be called after three seconds.

play11:47

But immediately also this console.log here

play11:50

will be called, all right.

play11:53

So this console.log here will not wait these three seconds

play11:57

for these set time out callback to finish.

play12:00

Okay, so immediately this variable is created,

play12:05

this set timeout function is run and console.log is run.

play12:09

And then after three seconds, this function here

play12:12

will be executed.

play12:13

And so let's see what happens to the n variable

play12:17

and to the perGroup variable that is in this function.

play12:23

So immediately we got this log and then after three seconds,

play12:27

we are boarding all 180 and there are three groups

play12:30

with 60 passengers each.

play12:33

Okay, it worked.

play12:36

And again, keep in mind that this callback function here

play12:39

was executed completely independently

play12:42

from the board passengers function.

play12:45

All right, but still the callback function

play12:48

was able to use all the variables that were

play12:51

in the variable environment in which it was created.

play12:55

So that's n and perGroup.

play12:59

And one more time, this is a clear sign

play13:01

of a closure being created, right.

play13:05

So the only way in which this callback function here

play13:08

can have access to the variables that are defined

play13:11

in the board passengers function

play13:13

that has long finished execution is if it created a closure.

play13:18

And so that's exactly what happened.

play13:20

And yeah, that's how we got access to perGroup

play13:24

and also this argument of the function

play13:26

so this wait here, right.

play13:29

So as I mentioned in the last lecture,

play13:31

the closure of course also includes the arguments

play13:34

and that's because they are really just

play13:36

a local variables in the function.

play13:39

And to finish, let's now also prove

play13:41

that the closure does in fact have priority

play13:45

over the sculpt chain.

play13:47

So here in the global scope,

play13:49

I'm also gonna create a variable called perGroup equals

play13:56

and then just some value here.

play13:59

And so if the scope chain had priority over the closure,

play14:03

then this callback function here

play14:05

would indeed use this variable here

play14:08

this global variable

play14:10

because we can imagine this function here

play14:12

basically being executed in the global scope, okay.

play14:16

So if it wasn't for the closure it would use this.

play14:20

So let me actually demonstrate that to you.

play14:22

So if I remove this variable,

play14:24

then it will be able to use the perGroup

play14:29

and data is here outside.

play14:31

So indeed now we get 1,000

play14:33

but then as we put it back here

play14:36

then the callback function will close over this variable.

play14:40

So it will close over

play14:41

the entire variable environment in fact.

play14:45

And so therefore it will then use this year first, right.

play14:50

And it did.

play14:52

So in fact, a disclosure even has priority

play14:56

over the sculpt chain.

play14:58

Okay, and with this,

play15:00

we finished these two lectures about closures

play15:04

and they hope that after this one,

play15:06

you are now a little bit better able to identify closures

play15:10

as they happen in your code or even here in my code

play15:14

throughout the codes

play15:15

because we will see some closures happening

play15:18

some more times in the future.

play15:21

Now all there's left to do is the coding challenge

play15:23

in the next video where you will be thinking

play15:26

about closures one more time.