Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Original: https://www.quora.com/Technically-how-do-JavaScript-closures-work
- Logan R. Kearsley
- , BS in CS and 12 years of work experience in programming.
- Updated 1 year ago ยท Author has 5.6K answers and 3.5M answer views
- Technically, how do JavaScript closures work?
- That depends on the implementation. There are several different ways of implementing closures, which interact with how the language implementor also chooses to implement mutable variables in nested scopes.
- The way that the V8 JavaScript engine (which powers node.js and Chrome) implements closures, however, is fairly typical, and not that hard to understand.
- First, however, let's look at a naive and inefficient, but functional, way that it could be done:
- Whenever a function is called, an object is allocated to hold all of that function's local variables. Typically, this is done on the stack, and we call that object (along with control flow information like the return address) a "stack frame", and it gets overwritten whenever a function returns and a new function is called after. But, you don't have to allocate variable storage on the stack. Rather than storing local variables in a stack frame, you can instead allocate an object on the heap for them instead. That way, local variables don't have to be deleted or overwritten when the function that created them returns--they can stick around as long as necessary, potentially forever.
- Then, whenever a closure is created, a reference to the object that stores all of the local context variables is stored with the closure. When all referencing closures are gone, the garbage collector can clean up that variable context object; but as long as the closure is accessible, it keeps access to the variables from the enclosing environment where it was created. Each context object also contains a reference to the context object for the next higher scope, so there's always a chain that lets the engine access variables from any arbitrarily higher scope for as long as they might be needed. It just has to remember, for each place where a variable is referenced, how many links back it has to go to find the right context object that holds the memory for that variable.
- Now, that works, but as I said it's not very efficient. Whenever a closure is created, it'll keep references to every context object for every higher lexical scope at that time, which can prevent a large amount of memory from being garbage collected for no reason. It also means that all variable access has to go through a pointer indirection to the heap, which slows things down. There are a lot of ways to improve it, but a fairly simple one, which V8 implements is as follows:
- When parsing the code, V8 keeps track of where each variable is referenced, and uses that information to divide the local variables for every function into two classes: those that are referenced inside a nested function (i.e., those that could be accessed from closures), and those that aren't (i.e., those that can never be accessed from a closure). That lets it allocate non-closure variables in a regular stack frame, which is automatically cleaned up and re-used on every new function call. But, if there are any closure variables, it allocates those in a context object on the heap, which may point to another context object from a higher scope (just like before) if variables from the higher scope are also closed over in the current scope.
- That means that the engine only has to keep around variables that might actually be used in the future on the heap, which saves a bunch of memory. Additionally, it only has to use pointer indirection for the variables that are actually used in closures--everything else can be accessed on the stack, which keeps it fast.
- In short: V8 figures out which variables are accessed in closures and stores them on the heap instead of on the stack, which means that all accesses to those variables are slowed down (which matters in tight loops and other hot code), but that they can survive indefinitely, for as long as any closure might need them.
Advertisement
Add Comment
Please, Sign In to add comment