Video details

Closures in Javascript: Inside the Execution Context & Scope with Codesmith


🖥Presented by Women Who Code Front End and Codesmith 👩‍💻 Speaker: Sara Powers, Senior Software Engineer/Lead Engineering Instructor at Codesmith ✨Topic: Closures in Javascript: Inside the Execution Context & Scope with Codesmith
In this event we will go under-the-hood of JavaScript, so you can confidently tackle new problems and work through blocks.
We will cover: - Understand how closure works under the hood (one of the most misunderstood but most powerful features of JavaScript) - Understand core JS runtime features - execution context, JavaScript execution model and the variable environment - Be able to wield, optimize and debug popular pro utility functions like once and memoize - Be able to implement the module pattern - one of the cleanest and most resilient design patterns in JavaScript
For our 💬 slack channel, 🎥 previous event recordings, 🗓 upcoming events, 💻 github repo and more check us out on
___ 💻 WWCode Digital Events:​ 🔎 Job Board:​​​​ 💌 Make a Donation:


As you know, my name is Sarah. I am a senior software engineer for Cooksmith and I also am the lead engineering instructor for the New York program. So my role is about 50% engineering, 50% teaching, which is great for me because I kind of get the best of both worlds way back in the day. My interest in code was kind of sparked by some my space days where I spend a bunch of time playing with my layout and things like that. I started seriously studying coding back in 2019 and that's what led me to Code Smith. I went through their program and I've been lucky enough to have been working for them ever since. I was an engineering fellow for a little while and then I became an instructor. I've also done some contract work for other companies. I also want to apologize. I'm in New York City and they're doing some sort of construction out on the street, so hopefully it's not too loud for you all, but heard a little bit about me. I'd love to tell you all a little bit about Codesmith before we get into all of the fun stuff with closure in JavaScript. So today we're going to be talking about closure. As you know, I work at Cosmic Tamar here from Coach Misty. I believe she dropped some links in the chat with some resources that we have available. And if you're wondering what codes Smith is, we have a few flagship programs. So we are a software engineering immersive program really geared towards people who are already maybe at a junior level in terms of engineering and wanting to level up their skills to be marketable for mid to senior level engineering jobs. So we have two flagship programs, a twelve week full time software engineering immersive. It is six days a week, very intense for three months. Everything is remote right now due to COVID, but we have one on Pacific time. That's our La campus, one on Eastern time, that's our New York campus, the one that I teach at. And we also have a one on Central time that is going to be remote no matter what the situation is. We also have a 38 week part time immersive program. This is a really great option for people who can't just quit their job and do nothing but code for three months. It's during the evenings on Pacific time, 508:00 P.m. Monday through Thursday, and then during the day on Saturday. We also have other resources and learning opportunities. We have some under the hood courses that are kind of four week modules really designed to dive very deep into one specific area of tech. We currently have one on Node, if you're interested on how JavaScript changes when it's working in the Node runtime environment rather than the browser. And we also have one on data structures and algorithms. Those are some prep programs, free workshops and learning resources and educational platform called CSX so lots of things to check out. Cosmic is kind of built on a few of these core philosophies. So we really our curriculum is duck over computer science fundamentals and kind of like things that you would learn if you are studying computer science. But most importantly, you get to put all of that into practical applications. So you learn things like React and Node and Express and SQL. We also have industry leading outcomes. So our average salaries are 120K for our residents that graduate out of New York and 116 for our residents that graduate out of La. My favorite thing about Cosmic, though, is the alumni network and the supportive community. We have an incredible community of residents, and we also very much tried to do our best to close the gender gap intact. We have a lot of additional resources and support and community events for our women and non binary residents and alumni, as well as those just like in our learning community. So I think there's like a shift in tech happening right now where there's a lot of people who are passionate about tech, but also passionate about people. And so Code Smith is really striving to kind of create that next generation of really empathetic engineers and most importantly, more diverse engineers, because as we know, diversity of perspective is going to be, I think, what changes the world. We have a quick side on outcomes. These are a few of our grads, and usually our grads get 86 of them get higher within 180 days from graduating. Like I said, the median salary is 120 in New York, 115 in La. Pretty impressive numbers in terms of outcomes. And so this is all verified data to you. You can check it on, sir, which is the Council for Integrity of Reporting and something, but they have all of the data as well as audited by somebody else. So I'm really excited to get into the high price closure. We're going to go under the hood this afternoon. Thank you to everyone who introduced themselves in the chat sharing where you're from. I really love that we have people just like from all over the world here. That's really lovely. We're going to get into closure because closure is one of the most esoteric JavaScript concepts. It's one of those things that's used in so many different features of JavaScript without people even realizing it. And I think that it's one of those things that if you really understand what's happening with closure under the hood, you're going to have such a strong mental model for how you can use it to your advantage in a bunch of different situations, whether it's a very simple sort of algorithm or something more complex when you're working with like Asynchronous code or Iterators and generators. Closure is what allows us to have these pro level functions like once and Memorize. Memoise and ones are very common interview questions. They are also just common out in the world, right? We use once if we want to function just only to be ran once, and we need some sort of persistent memory to keep track of that that's not in an external variable. Memoize allows us to cache certain calculations if they're computationally expensive, so we don't have to keep doing those calculations. A lot of JavaScript design patterns use closure, so the module pattern NPM, if you're familiar with downloading libraries and packages in Node, all of that structure is built off of closure. It also allows us to build iterators and maintain States in an asynchronous world. So asynchronous JavaScript is kind of how we have the modern Internet, the way we experienced it now, and it wouldn't be possible without closure. So this is kind of what we're diving into today. So before we get into any the more advanced code, we're going to be talking first about just some principles in JavaScript. This workshop truly is all levels. I don't know what level everyone here is at, whether you are working as an engineer or you're just learning, but just there are some key things to kind of remember about JavaScript when we're going through these examples so that we are able to keep a strong mental model of how JavaScript is operating under the hood. So functions first and foremost, we know that we have functions whenever we want to bundle up some set of instructions, some piece of code, slap a label on it, and be able to use it later on, right? So we don't have to repeat ourselves when we want to execute the same functionality. So functions are ways that we can define specific instructions and then call invoke execute, run it later. Javascript is a synchronous language, which means it's executed line by line, right? So we're going to be keeping track of the thread of execution while we're diagramming out code. Because JavaScript reads code line by line, left to right, it's important that we realize sort of where we are, what function execution context we're in, what piece of code is being run as we go through these examples. And last but not least, we need some memory, right? We need some places to actually store any data that we might need to access later. And so when we have memory, we have a global memory that exists within the global execution context of JavaScript. And then we have local memory every time we invoke a function. That local execution context also creates a little memory for us to keep track of. So in this example, we are declaring a variable numb, assigning at the value of three. We are defining a function multiplied by two, which takes in one argument input number, and we are declaring a constant result, multiplying the input number by two, and returning that result. So we are invoking the function by using these parentheses, and we are storing the results of that function to global memory using these labels output and new output. So with that in mind, with those kind of basic foundational principles and pieces in mind, it's important to also know that functions can have multiple names in JavaScript. So functions are just a set of instructions, the block of code that we want to save for later. And we can give that set of instructions multiple different labels because there are two different price. When we're defining a function, there is the function name, which is the actual label we give it. That's what we define right here after the keyword function. And then there's the definition, and the definition includes the parameter and everything that we've defined as part of the function definition. So behind the scenes in JavaScript, what happens is these functions are composite data type in JavaScript. When we give it a label, it's essentially just acts as a pointer to some place in the heap, which is just kind of like the JavaScript memory, right? So I can rename a function if I need to give it a different label, and it will point to the same function in memory. So let's take a look at that in action. I'm going to be diagramming a lot today throughout the workshop. I can't remember who said it, but you can use the Q and A to ask questions. I'll be taking questions throughout if you have any, and then we also will have some time for questions at the end. But let's diagram out this bit of code to start. So this is kind of like my blank slate. I have a place for global memory. I have a place for the call stack. If you're unfamiliar. The call stack is how JavaScript keeps track of what execution context we're in. And so since we're initially starting to run our code, we are in the global execution context, and the first frame on the call stack is going to be our global execution context. And now we're going to diagram out this code line by line. So what's happening on this first line of code, right, is we are declaring a function multiply by two. Javascript tends to hoist function definition. So JavaScript will immediately just save this function definition to memory without actually running any of the code inside of it. Right? Because we're just defining the function, we are not executing it yet. So I'm going to add to my global memory label multiplied by two, and I'm going to assign it a function definition. That little box with an F is going to be my set of logo for function definition. So we know that in our global memory we have a label multiply by two that points to a function definition. On this next line of code, we're declaring a constant output in global memory, and we are assigning it the value of invoking multiply by two with the argument of sign. So since we are invoking a function, we are going to open up a new local execution context, which means a few things happen in JavaScript. When we execute a function, we push the function to the call stack so that JavaScript knows what execution context we are currently executing code in. So we are going to push multiply by two to the call stack. We are going to open up a local execution context. So this is for multiply by two with the argument nine. In our local execution context, we know that in addition to global memory, we also have local memory. So we're going to set up a little local memory in here so we can keep track of any variables defined in the actual function definition. So we have local memory. And now that we have all this, set up the thread of execution which we will call Tow. Very affectionately, thread of execution is going to move in to the multiplied by two local execution context. Once we're finally here, that's when we can actually run the code in the function definition line by line. So we get to this first line right here, Const result and assigning it the value of input number times two. So we're declaring a variable result inside of local memory and we are assigning it the value of input number multiplied by two, which since we know our argument is nine is going to be 18. We then hit this line where we return results. So how JavaScript is going to look for that variable? It's going to check its local memory and say hey, is there a label in here? Results there sure is. So it will grab this value of 18 and return it to where the function was executed. So output is where we executed the function. That value gets returned out. And since we are returning out of the function, a few things happen. We pop multiply by two off the call stack. We garbage collect this execution context, which is just JavaScript's way of deleting it. And the thread of execution goes back into the global execution context. Oh no, my erasing. Oh, there we go. I noticed that it didn't reflect on the screen when I had erased everything. So we then move on to this line of code is JavaScript is going to check hey, do I have anything in my global memory by the name multiplied by two? And sure enough, it's going to look to global memory and it's going to find this function definition right here. So JavaScript just grabs the function definition and that's what we assign to new label for multiply by two. So it's the exact same function definition. We've just given it a different label. So this new label for multiplied by two has no idea that it used to be named multiplied by two. So when we get to this line where we're declaring fresh output, we declare constant fresh output and we are invoking a function which means we need to push I'm just going to call it new label for sake of space to the call stack, we are going to open up a new local execution context. The wordy one. It was an argument of ten. When we get into this execution context, we're declaring a local memory. We are now in the body of the function definition. The thread of execution moves in here and we're declaring a constant result in local memory and we're assigning it the value of input number times two, which is 20. We then return that result back out to our global memory. We garbage collect this execution context and we pop new label off the call stack. We've then reached the end of our code and that's that. So really, this is just to illustrate that we can give functions different labels and they still remain the same in terms of function definition. New label for multiply by two and multiply by two are pointing to the exact same place in memory where that actual function definition is stored. Closure. As we saw, every time we closed out or garbage collected one of those local execution contexts, our entire local memory would be deleted, right? It would be garbage collected. We'd have no memory of what that result variable was in local memory. So when our functions get called, we create this live store of data that's our functions execute inside of our functions. Execution context. When the function is finished executing, the local memory is deleted, right? But wouldn't it be handy, wouldn't it be nice if our functions could hold on to that live data and that state in between executions? So this would allow our functions to have an associated cache or persistent memory, which is huge. It's groundbreaking, but it all starts with us returning a function from another function. So in JavaScript, functions can be returned from other functions, right? Because there are two parts to a function. There's the label and there's the definition and we can return the definition as a value from a function. So let's see what that looks like in action. We are going to diagram this out again. A different bit of code this time though. So we are in our global execution context. We push global to the call back and then we're going to start going through our code. So the first line of code we are declaring a function create function. I'm going to go ahead and store that in global memory. Rather this function definition, the next line of code, I'm declaring a constant generated funk. So I find this label generated funk and the value of generated funk is going to be the result of executing create function, whatever that return. So since we are executing a function, a couple of things have to happen. We have to push create function to the call stack. So create function is pushed to the call stack. We have to open up a local execution context. We got to get our local memory set up and then the thread of execution is going to move into the local execution context of Create function. So once we're here, we see on the first line of this function definition we are declaring a function multiply by two and that's going to be stored in local memory. So we give a label multiply by two and we assign it the value of a function definition. The next line of code that we hit is this line right here where we're returning whatever is stored at the label multiplied by two. Now how JavaScript finds this is it's always going to check local memory first, right? So it checks first. It's local memory. It finds that there is a label multiplied by two and what exists there is this function definition. That's what we're returning. We're returning only the definition. So see if we can just copy this. Now generated Funk, its value is the function definition that Create function returned. Since we returned out of this function, we have to garbage collect our execution context. The thread of execution goes back into the global execution context and we pop create function off the call stack. This next line of code right here, we declare a constant result. Then we assign it the value of invoking generated funk with the argument of three. So since we're invoking a function, we have to push it to call stack. We have to open up a new local execution context and we need a local memory and the threat of execution is going to go into this local execution context. So once we're in here, remember JavaScript has stored the function definition of multiply by two, though it doesn't know it's called multiply by two anymore. Now we know it as generated funk, but we're going to match up our parameters with our arguments. So the parameter is NUM and we have passed in three, right? So NUM is going to have the value of three and then we actually execute the code which is just returning NUM times two. So we are going to return six to our label results. Then we're returning out of the function, we garbage collect the thread of execution, goes back into global and we pop generated Funk off the call stack. Since we've reached the end of our code, that's all there is there, right? So this is the very beginning of how we can execute closure by realizing that we can return a function definition from a function in JavaScript. This is something that you can't do in some other languages. This is one of those things that's like not necessarily unique to JavaScript, but almost we can also call a function in the same function call it was defined in. So understanding these intricacies of how scope is working in JavaScript, where JavaScript is checking for certain labels and pieces of data is really important to realizing why it is closure works and how it is closure works. So we're going to diagram this out because as you can see on this slide where you define your function determines what variables your function has access to when you actually invoke or call that function. So first and foremost, right, we are in the global execution context. So we'll put that label up there, we will push global to the call stack. And then the first thing that's happening is we are defining a function outer in global memory. The function definition then the next line of code, we are just invoking outer. So since we're invoking a function, we need a new local execution context. We need to push outer to the call stack, we need local memory, and then we can actually we also need a set of execution, and then we can actually execute this function line by line, right? So we are going to first start off by declaring variable counter and assigning it to value zero. Then we're declaring a function called increment counter and we're storing that in local memory as well. Can I fit increment counter? It's all these long function names. All right, we have a function definition increment counter. This next line of code right here we are invoking increment counter, which means another execution context. And we need another stack on the call frame or another frame on the call stack. So we are going to push increment counter to the call stack. We are going to open up another execution context, this one for increment counter. And then we need a local memory inside of increment counter. A bit of execution is going to move into this execution context. So we see that the body of increment counter, the function definition is just incrementing counter by one counter. Plus plus. However, we know that JavaScript normally is going to look in its own localized variable environment, right? So it checks increment counters local memory. Does it find a variable counter? No, it doesn't. And so it checks Outer's local memory because this is where this function was defined, right? We defined this function inside of the context of outer. And since we defined the function in outer, we have access to these variables, right? So it finds the variable counter in outer local memory. And it goes, Great, okay, I guess I'll increment that by one. So we increment counter by one counter is now one, right? That's the last line of code inside of increment counter. So we garbage collect increment counters execution context. The threat of execution goes back into outer. Increment counter gets popped off the call stock. And then we realize, okay, this is actually the last line of code inside outer too. Which means that we also need to pop outer off the call stock and garbage collect outer as well. So this is a little hint as to how closure is working, right? Instead of looking only in its own local environment and the global environment, it also looked to where it was defined and what variables were there. So let's see that in action. I see that there's a question in the Q and A. Don't we need to invoke increment counter funk here we call it outer funk. Yeah, Priya. So we actually invoke increment counter inside of this function outer. So we defined it inside outer and then we invoked it right after we defined it inside outer. Okay, I'm going to keep on moving. So we can also call a function outside of the function call which it was defined. And remember where you define your function, it impacts what variables you have access to. So let's see it in action. Okay, we have to get our global execution context set up. We are going to push global to the call stack and then we are going to define our function outer. Right? That's the first line of code is we are defining this function outer. Then we have this constant my new function. Which value is the evaluated result of invoking outer. So we'll store my new function in global memory. And in order to figure out what the value of my new function is going to be, we have to invoke outer. So we are going to create a new execution context for outer. We are going to push outer to the call stack, the thread of execution is going to move into outer and we are going to set up. So once we have set all of that up, we actually get into the body of the function definition. The first line of code, we are declaring a variable counter and assigning it the value of zero. So we're storing that in local memory. Then we're declaring a function increment counter our old friend from the last example and we are storing that in local memory. Then the next line of code is this one right here where we're returning whatever we're storing at the label increment counter back into the global execution context. So if we look to our local memory we see that we are returning this function definition. So we're going to return that out to the global execution context at our label my new function. Now this is where closure comes into play and this is where it gets fun. So we have executed or we have finished executing the code of outer. Right? So we garbage collect this execution context. The thread of execution goes back into global and we pop outer off the call stack. Now remember when we garbage collect and execution context, that means that its local memory is gone. So our next line of code is right here where we invoke my new function. So since we're invoking a function, we need to push my new function to the call stack. We are going to open up a local execution context. So my new function, we are going to set up our local memory and then the thread of execution is going to go into my new function. So once we're in here, remember that this is the function definition of my new function, right? It used to be known as increment counter within the context of outer. Now it's known as my new function. And this is the only line of code in that function definition. So knowing what we know about JavaScript logically it will check first it's local memory for the variable counter. We don't have anything in local memory for the context of my new function. So if we were following JavaScript standard rules, the next place it would look would be global memory. And as you can see, we don't have a variable counter in global memory. And this is where closure comes into play. So when we returned this function definition of increment counter from outer, we returned the function definition. But it also brought along some other things with it. And for simplicity's sake, right now I'm going to call it a backpack. So my new function, when that function definition of increment counter was returned, it brought along this backpack of some other stuff with it. So in addition that's my terrible drawing of a backpack. Let's give it a fun color to make it look a little bit better. So in addition to the function definition, it also brought along this backpack of some things it thought it might need. Remember, this is all about functions having access to the variables that were present where they were defined. So if we think back to when we first defined increment counter, we had a variable counter within the context of outer. And so increment counter brought that variable counter along in its backpack. So inside of this backpack we currently have counter with the value of zero. So when we're inside this execution context of my new function, it first is going to check its local memory. We don't have a variable counter before it checks global. It is going to look inside its own backpack. That data that it brought along with it. And so it finds it in here, right? Because it was present in the context in which increment counter, now known as my new function was defined. And so we are going to increment counter to one that's the last line of code, right? So we are going to garbage collect this execution context and pop my new function off the call back. The next line of code is another invocation of mining function. And so we are going to open up another local execution context. We're going to push my new function to the call stack. But if execution is going to move into my new function, then we also are going to set up a local memory, right? Remember that this is the function definition that we are executing, right? Counter plus plus, it is going to look first in its own local memory. It doesn't find it. So then it is going to pivot and look in its backpack. We know that we have a variable in there called count share and it starts off with a value of one. We are then going to increment it to two the end of our code. We are going to garbage collect this execution context and pop this off the call stack. This is how closure works. This is what makes closure such a cool concept. It's like my favorite thing in JavaScript, right? It gives us persistent data across multiple execution contexts without having to use a global variable. So this counter variable is only accessible inside this backpack. I don't have to worry about somebody else defining something called counter in the global context and overwriting it or messing with it. It's only accessible within the context of executing my new function. So the backpack, when increment counter is defined inside outer, it gets that bond that link to the local memory inside of outer. When we return increment counters code, we give it a new name, my new function. But we maintained that bond to the environment in outer, so it gets returned out attached on the back of increment counter function definition. That's why I like the term backpack. When I was kind of like diving into closure under the hood in my own learning journey, it was really helpful for me to imagine literally the function definition, bringing a backpack along with it with all the variables it might need. This is that bond that it maintains even after the execution context is garbage collected. So when we run my new function in the global execution context, it's going to first look in its own local memory, but then it looks in its backpack to see if it has anything in there. So what can we call this backpack? We can call it a lot of different things. So closed over variable environment is a name that you could use for it. That's kind of where closure comes from because it is a variable environment, but it's closed over, right? It's not easily accessible. It's only accessible in relation to that function. Definition. My personal favorite Besides backpack because that's so fun, is Persistent Lexical Scope Reference data. It is a mouthful, but like if you're in a job interview and they ask you about closure and you start talking about it and you're like, you know what I think a more precise name is for this persistent Lexical scope reference data. They're going to hire you on the spot. Backpack. Like I said, it's like a fun terminology. It was really helpful for me to get that visual. And closure is what we mostly know it as. So the backpack or closure of live data is attached to increment counter, then to my new function through a hidden property known as scope double bracket scope. And this persists when the inner function is returned out. So I want to be super clear. You can't do things like my new function, double bracket scope you can't access in this way. I think there might be a way in Chrome to see what is hiding in that backpack and that closure. But typically you can't see what's in there so you can't access it this way. If you tried to console log double bracket scope, you get anything. But this is a hidden property where this variable environment is living, and this persists when the inner function is returned out. So this is how we get access to this persistent variable store is by returning a function from another function, you can have more than one backpack. So I'm going to copy this really quickly because I know we are getting close on time. And go back to our diagram here and let's just see what like a separate backpack would look like in action. So we have this example code. The beginning part is exactly the same as what we just looked at, right? We created a my new function and assigned it the evaluated result of invoking outer. And then that's how we incremented our counter in our backpack for my new function. And now counter is equal to two. We're going to declare another constant, another funk. It also is going to be a function definition, right? Because we are going to invoke outer, we are going to open up a local execution context for outer. We are going to open up a local memory, and then we are going to declare a variable counter and assign it a value of zero. We are going to declare it a variable increment counter as a function definition. And then we are returning this function definition when we return on the outer. So we garbage collect this execution context. But we know now that when we return that function definition out, it's bringing along a backpack, right? It's bringing along the variables that were present when it was defined. So it's the same it's the same function definition of counter plus plus, we don't need that circle there. But it brought along another backpack too. We'll give it a different color. And inside this backpack we have counter with the value zero because we invoked outer again, right? So this is a separate backpack. This is a separate closure than our first invocation of outer. And so you can have multiple persistent memories for the same contact for the same function definition. Because every time you invoke outer it's creating a new place in memory for that function. Increment counter, right? So it's like a new version of essentially it's not pointing to the exact same function definition. So when we invoke another funk or another function, we need to push it to the call stack. We set up our local memory and it looks first and it's local memory four counter. Does it find it? No, it doesn't. So it looks in the backpack. It's going to increment counter to one. Then we garbage collect this, pop another function of the call stack and does it one more time. We open up a new execution context for another funk. We push another funk to the calls back. We set up our local memory in a local memory for counter doesn't find it. It looks in a backpack. Sure enough, it does find it. Counter is now two. We have reached the end of our code. So you can have separate backpacks individual backpacks for the same function, you can have multiple different persistent memories. This is how we have persistent data across function calls. The closure is an extremely powerful tool. We can have individual backpacks if we run out or again if we store the returned increment counter function definition in another function. We just thought that it would be a completely separate backpack than that first one. Closure gives our function persistent memories. This is huge. This gives us a whole new toolkit for how we can write quality code. Right. This gives us a way to avoid global variables. It gives us the ability to create helper functions like once. Right. Because you can store something in the backpack that keeps track of whether or not that function was invoked or not. It allows us functions like memoised because you can have a persistent cache with a function definition that's only accessible through that function. It allows us to create iterators and generators, which are just ways for JavaScript to iterate through arrays and data sets in a different way than the standard like for loop. But it's possible because of closure. The module pattern NPM modules importing and exporting when you're building out a repository that's built on closure. And perhaps, I think most importantly asynchronous JavaScript is built on closure. So anytime you schedule a callback or use a promise, right, it has access to the variables that were present when you define that callback function. And that's why you can mutate some data once you get a response from a network or once your set timeout is through, or whatever it may be because it brought along all that data that you needed from it. So closure makes a lot of things incredibly possible for us in JavaScript. I know we're almost out of time, but if anybody has any questions about what we covered today, I'm happy to answer. I'm looking at the chat. To catch you up on that. Everyone is enjoying this visual sort of explanation. I think it's a really good one. Yeah. We all love backpack. Backpack changed the game for me. I feel like I really couldn't visualize closure before I learned the term backpack. All right. If you have any questions, you could put it in the Q and a few like, kind of closing up slides. Yeah. The execution context is also really interesting. If you're like, man, I'm happy to just dive into something. We do have a couple of under the head courses coming up at Codesmith, data structures, algorithms, and node data structures. Algorithm starts November 15. Node starts November 29. Feel free to check that out on our website. This is like a four week deep dive into these concepts. We also have something called CSX. If you've been working on closure, we have a whole section of CSX. It's a free online learning platform. We have a whole section on closure. There's like a lot of closure challenges and algorithms to help you strengthen those skills. We also have some courses, JavaScript for beginners, PS prep, and we have free weekly workshops. I teach a lot of them, so I hope you come. We also have women's workshops on Wednesday night, so I highly recommend coming to those. We specifically tried to make tasks more accessible for women. I think it's so important. It's something I'm very passionate about. So we do have scholarships for people who are female identifying. We have women only free workshops. We have mentorship programs if you're in the admissions or application process. We also have a special scholarship for our Women who Code community. So I believe. Tamar, did you share this link for the women who code specific scholarship in the chat? Yes, I did. I think it's one of those ones. Amazing. So we do have a specific scholarship for women who code. If that's something that you're interested in pursuing and feel free to email me. My email is on here. My LinkedIn is on here. I love to talk about code. I especially love to support women in their coding journey, so please don't hesitate to reach out if you want to connect. Also, if you go to Cosmic. Io, it has info about all of our programs and also our free workshops. I'd love to see you again. I'm going to check the chat just to make sure. Katie, thank you so much. Any final questions before we wrap up? I think we're going to end right on time. I don't know how we spent that I have a tendency to go over, so I'm just happy we wrapped it up on time. It's great. Sarah, everyone has been leaving feedback. It was great. I think this was a great way to learn these sort of technical topics. And like Path, he already asked another workshop on execution context. So yes, let us know if you'd be happy to do that. Okay. Sounds good. Thank you. Thank you to everyone who came. It was so lovely to have you here. I hope everybody has a great rest of your Tuesday and I hope to see you in another workshop sometime. Great. Awesome. Just a quick reminder. So once you log out to the attendees, you will get a survey. We are trying out new zone features so it should just take like a minute or less. So let us know the feedback about this. We would really appreciate if you could answer the short 1 minute survey. Awesome. Thank you so much. Cynthia, thank you to everyone who came. Thank you. Women Who Code hope to see you again soon. Yes. Thank you all. And thank you, Sarah. Thank you so much. And thank you for, Smith bye everyone. See you have a great rest of the day.