Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where does a JavaScript closure live?

I wrote this code to teach myself about JavaScript closures:

function1 = function(){   var variable = "foo"   var function2 = function(argument){     console.log(variable + argument);   }   return function2 }  function3 = function1(); function3("bar"); 

This prints "foobar" as expected. But where does the variable live?

Does it become a property of function3, or stored somewhere else in function3? Does JavaScript traverse some kind of closure chain, similarly to how it traverses the prototype chain? Is it stored in memory somewhere else?

I am trying to understand this more deeply.

like image 866
Wylliam Judd Avatar asked May 27 '16 20:05

Wylliam Judd


People also ask

Where are closures stored in memory?

So. With this in mind, the answer is that variables in a closure are stored in the stack and heap.

How does JavaScript closure work?

A Closure is a combination of a function enclosed with references to its surrounding state (the lexical environment). In JavaScript, closures are created every time a function is created at run time. In other words, a closure is just a fancy name for a function that remembers the external things used inside it.

What is hosting scope and closure in JavaScript?

JavaScript allows us to nest scopes, and variables declared in outer scopes are accessible from all inner ones. Variables can be globally-, module-, or block-scoped. A closure is a function enclosed with references to the variables in its outer scope.

How is a closure created?

A closure is created every time an enclosing outer function is called. In other words, the inner function does not need to return for a closure to be created. The scope of a closure in JavaScript is lexical, meaning it's defined statically by its location within the source code.


1 Answers

tl;dr:

where does the variable live?

In the environment it was defined in.

Does it become a property of function3, or stored somewhere else in function3?

No.

Does JavaScript traverse some kind of closure chain, similarly to how it traverses the prototype chain?

Yes.

Is it stored in memory somewhere else?

Yes.


tl;dr 2:

Functions keep a reference to the environment they are created in. When a function is called it creates a new environment whose parent is the environment the function kept the reference to.


Longer explanation:

Whenever a function is executed a new lexical environment is created. The environment has two "fields": an environment record where all the variables are being tracked and a outer lexical environment that refers to, as the name suggested, to the "parent lexical environment".

So when we your code example is evaluated, the initial state of the memory (before executing anything) might look like this (simplified):

+-(Global) lexical environment-+     +-Environment Record-+ +-------------+----------------+     +---------+----------+ | Environment |       *--------+---> |function1|undefined | |   Record    |                |     +---------+----------+ +-------------+----------------+     |function3|undefined | |    Outer    |                |     +---------+----------+ |   lexical   |    (empty)     | | environment |                | +-------------+----------------+ 

The global environment doesn't have any outer environment because it is at the top. function1 and function3 are two bindings that haven't been initialized yet (the assignment wasn't evaluated yet).

After creating the function (evaluating function1 = function() { ... }), the memory looks like this:

            +------------------------------------------------------------------------+             |                                                                        |             v                                                                        | +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+ +-------------+----------------+    +---------+----------+     +---------------+-----+---+ | Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   | |   Record    |                |    +---------+----------+     +---------------+---------+ +-------------+----------------+    |function3|undefined |     |     name      |function1| |    Outer    |                |    +---------+----------+     +---------------+---------+ |   lexical   |    (empty)     | | environment |                | +-------------+----------------+ 

Now function1 has a value, a function object. Function objects have multiple internal (e.g. [[Environment]]) and external (e.g. name) properties. As the name implies, internal properties cannot be accessed from user code. The [[Environment]] property is very important. Notice how it refers back to the lexical environment the function was created in!

The next step is executing function3 = function1(), i.e. calling function2. As I said at the very beginning, whenever a function is executed a new lexical environment is created. Let's look at the memory just after entering the function:

               +------------------------------------------------------------------------+                |                                                                        |                v                                                                        |    +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+    +-------------+----------------+    +---------+----------+     +---------------+-----+---+    | Environment |       *--------+--->|function1|          +---->|[[Environment]]|     *   |    |   Record    |                |    +---------+----------+     +---------------+---------+ +> +-------------+----------------+    |function3|undefined |     |     name      |function1| |  |    Outer    |                |    +---------+----------+     +---------------+---------+ |  |   lexical   |    (empty)     | |  | environment |                | |  +-------------+----------------+ | | | |  +-----lexical environment------+    +-Environment Record-+ |  +-------------+----------------+    +---------+----------+ |  | Environment |       *--------+--->|variable |undefined | |  |   Record    |                |    +---------+----------+ |  +-------------+----------------+    |function2|undefined | |  |    Outer    |                |    +---------+----------+ |  |   lexical   |        *       | |  | environment |        |       | |  +-------------+--------+-------+ |                         | +-------------------------+ 

This looks very similar to the structure of the global environment! We have a lexical environment that has an environment record with two unintialized bindings. But the big difference now is that "outer lexical environment" points to the global lexical environment. How is that possible?

When calling function1 and creating a new lexical environment, we set the value of the new environments "outer lexical environment" field to the value of function1's [[Environment]] field. This is were the scope chain is created.

Now, after executing function1, the memory has this structure:

               +------------------------------------------------------------------------+                |                                                                        |                v                                                                        |    +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+    +-------------+----------------+    +---------+----------+     +---------------+-----+---+    | Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   |    |   Record    |                |    +---------+----------+     +---------------+---------+ +> +-------------+----------------+    |function3|   |      |     |     name      |function1| |  |    Outer    |                |    +---------+---+------+     +---------------+---------+ |  |   lexical   |    (empty)     |                  | |  | environment |                |                  | |  +-------------+----------------+                  +-------------------------+ |                                                                              | |             +----------------------------------------------------------------+--------+ |             v                                                                |        | |  +-----lexical environment------+    +-Environment Record-+                  v        | |  +-------------+----------------+    +---------+----------+                           | |  | Environment |       *--------+--->|variable |  'foo'   |     +-----Function Object-+---+ |  |   Record    |                |    +---------+----------+     +---------------+-----+---+ |  +-------------+----------------+    |function2|    *-----+---->|[[Environment]]|     *   | |  |    Outer    |                |    +---------+----------+     +---------------+---------+ |  |   lexical   |        *       |                               |     name      |function2| |  | environment |        |       |                               +---------------+---------+ |  +-------------+--------+-------+ |                         | +-------------------------+ 

Similar like function1, function2 has a reference to the environment created by calling function2. In addition, function3 refers to the function we created because we return it from function1.

Last step: calling function3('bar'):

               +------------------------------------------------------------------------+                |                                                                        |                v                                                                        |    +-(Global) lexical environment-+    +-Environment Record-+     +-----Function Object-+---+    +-------------+----------------+    +---------+----------+     +---------------+-----+---+    | Environment |       *--------+--->|function1|    *-----+---->|[[Environment]]|     *   |    |   Record    |                |    +---------+----------+     +---------------+---------+ +> +-------------+----------------+    |function3|   |      |     |     name      |function1| |  |    Outer    |                |    +---------+---+------+     +---------------+---------+ |  |   lexical   |    (empty)     |                  | |  | environment |                |                  | |  +-------------+----------------+                  +-------------------------+ |                                                                              | |             +----------------------------------------------------------------+--------+ |             v                                                                |        | |  +-----lexical environment------+    +-Environment Record-+                  v        | |  +-------------+----------------+    +---------+----------+                           | |  | Environment |       *--------+--->|variable |  'foo'   |     +-----Function Object-+---+ |  |   Record    |                |    +---------+----------+     +---------------+-----+---+ |+>+-------------+----------------+    |function2|    *-----+---->|[[Environment]]|     *   | || |    Outer    |                |    +---------+----------+     +---------------+---------+ || |   lexical   |        *       |                               |     name      |function2| || | environment |        |       |                               +---------------+---------+ || +-------------+--------+-------+ ++------------------------+  |  | +-----lexical environment------+    +-Environment Record-+  | +-------------+----------------+    +---------+----------+  | | Environment |       *--------+--->|argument |  'bar'   |  | |   Record    |                |    +---------+----------+  | +-------------+----------------+  | |    Outer    |                |  | |   lexical   |        *       |  | | environment |        |       |  | +-------------+--------+-------+  +------------------------+ 

Similar here, a new environment is created and its "outer lexical environment" field points to the environment created when function1 was called.

Now, looking up the value of argument is straightforward, because it exists in the environment's own record. But when looking up variable, the following happens: Since it doesn't exist in the environment's own record, it looks at its "outer lexical environment"'s record. It can do that because it has a reference to it.

like image 86
Felix Kling Avatar answered Sep 20 '22 16:09

Felix Kling