Table of Contents

How JavaScript closures work under the hood

You're reading the original article in English. You can as well read the translations:

If you have translated the article to different language, please leave a comment or write me an email, so that I can update the list. Thank you.


I've been using closures for quite some time already. I learned how to use them, but I didn't have clear understanding of how closures actually work, and what's going on behind the scenes when I use them. What the closure is exactly, to begin with? Wikipedia doesn't help very much. When it is created and deleted? What the implementation should look like?

"use strict";
 
var myClosure = (function outerFunction() {
 
  var hidden = 1;
 
  return {
    inc: function innerFunction() {
      return hidden++;
    }
  };
 
}());
 
myClosure.inc();  // returns 1
myClosure.inc();  // returns 2
myClosure.inc();  // returns 3
 
// Ok, very nice. But how is it implemented,
// and what's going on behind the scenes?

And when I eventually got it, I felt excited and decided to explain it: at least, I will definitely not forget it now. You know,

Tell me and I forget. Teach me and I remember. Involve me and I learn.
© Benjamin Franklin

And, after all, while I was reading existing explanations of closures, I tried hard to imagine visually how things relate to each other: which object references others, which one inherits from another, etc. I failed to find such useful illustrations, so, I decided to draw my own.

I assume that the reader is already familiar with JavaScript, knows what is a Global Object, knows that functions in JavaScript are “first-class objects”, etc.

Scope chain

When any JavaScript code is executing, it needs some place to store its local variables. Let's call this place as a scope object (some refer to it as a LexicalEnvironment). For example, when you invoke some function, and function defines local variables, these variables are saved on the scope object. You can think of it as a regular JavaScript object, with the notable difference that you can't refer to the whole object directly. You can only modify its properties, but you can't refer to the scope object itself.

This concept of scope object is very different from, say, C or C++, where local variables are stored on stack. In JavaScript, scope objects are allocated in heap instead (or at least they behave like this), so they might stay allocated even if function already returned. More on that later.

As you might expect, scope object might have parent. When the code tries to access some variable, interpreter looks for the property of current scope object. If the property doesn't exist, interpreter moves to the parent scope object, and looks there. And so on, until the value is found, or there's no more parent. Let's call this sequence of scope objects as a scope chain.

The behavior of resolving a variable on scope chain is very similar to that of prototypal inheritance, with, again, one notable difference: if you try to access some non-existing property of regular object, and prototype chain doesn't contain this property either, it's not an error: undefined is silently returned. But if you try to access non-existing property on the scope chain (i.e. access non-existing variable), then ReferenceError occurs.

The last element in the scope chain is always the Global Object. In top-level JavaScript code, scope chain contains just a single element: the Global Object. So, when you define some variables in top-level code, they are defined on Global Object. When some function is invoked, scope chain consists of more than one object. You might expect that if function is called from top-level code, then scope chain is guaranteed to contain exactly 2 scope objects, but this is not necessarily true! There might be 2 or more scope objects; it depends on the function. More on that later.

Top-level code

Ok, enough theory, let's try something concrete. Here is a very simple example:

my_script.js
"use strict";
 
var foo = 1;
var bar = 2;

We just create two variables in top-level code. As I mentioned above, for top-level code, scope object is a Global Object:

In the diagram above, we have execution context (which is just top-level my_script.js code), and the scope object referenced by it. Of course, in real world, Global Object contains lots of standard- and host-specific stuff, but it's not reflected in the diagrams.

Non-nested functions

Now, consider this script:

my_script.js
"use strict";
var foo = 1;
var bar = 2;
 
function myFunc() {
  //-- define local-to-function variables
  var a = 1;
  var b = 2;
  var foo = 3;
 
  console.log("inside myFunc");
}
 
console.log("outside");
 
//-- and then, call it:
myFunc();

When the function myFunc is defined, myFunc identifier is added to the current scope object (in this case, Global object), and this identifier refers to the function object. The function object holds function's code as well as other properties. One of the properties that we're interested in is an internal property [[scope]]; it refers to the current scope object, that is, the scope object that was active when the function is defined (again, in this case, Global object).

So, by the time the console.log(“outside”); is executed, we have the following arrangement:

Again, take a moment to reflect on it: the function object referred by myFunc variable not only holds the function code, but also refers to the scope object which was in effect when the function is defined. This is very important.

And when the function is invoked, new scope object is created, which keeps local variables for myFunc (as well as its argument values), and this new scope object inherits from the scope object referenced by the function being invoked.

So, when myFunc is actually invoked, the arrangement becomes as follows:

What we have now is a scope chain: if we try to access some variable inside myFunc, JavaScript will try to find it on the first scope object: myFunc() scope. If the lookup fails, then go to the next scope object in the chain (here, it's Global object), and look there. If requested property is not a part of all scope objects in the chain, then ReferenceError occurs.

For example, if we access a from myFunc, we get value 1 from the first scope object myFunc() scope. If we access foo, we get value 3 from the same myFunc() scope: it effectively hides the foo property of the Global object. If we access bar, we get value 2 from Global object. It works pretty much like prototypal inheritance.

It's important to note that these scope objects are persisted as long as there are references to them. When the last reference to some particular scope object is dropped, this scope object will be garbage-collected on occasion.

So, when myFunc() returns, nobody references myFunc() scope anymore, it gets garbage-collected, and we end up with previous arrangement again:

From now on, I won't include explicit function objects in the diagrams, since diagrams become too overloaded otherwise. You already know: any reference to a function in JavaScript refers to function object, which, in turn, has a reference to the scope object.

Always keep this in mind.

Nested functions

As we've seen from the previous discussion, when function returns, nobody else references its scope object, and so, it gets garbage-collected. But what if we define nested function and return it (or store somewhere outside)? You already know: function object always refers to the scope object in which it was created. So, when we define nested function, it gets reference to the current scope object of outer function. And if we store that nested function somewhere outside, then the scope object won't be garbage collected even after outer function returns: there are still references to it! Consider this code:

my_script.js
"use strict";
 
function createCounter(initial) {
  //-- define local-to-function variables
  var counter = initial;
 
  //-- define nested functions. Each of them will have
  //   a reference to the current scope object
 
  /**
   * Increments internal counter by given value.
   * If given value is not a finite number or is less than 1, then 1 is used.
   */
  function increment(value) {
    if (!isFinite(value) || value < 1){
      value = 1;
    }
    counter += value;
  }
 
  /**
   * Returns current counter value.
   */
  function get() {
    return counter;
  }
 
 
  //-- return object containing references
  //   to nested functions
  return {
    increment: increment,
    get: get
  };
}
 
//-- create counter object
var myCounter = createCounter(100);
 
console.log(myCounter.get());   //-- prints "100"
 
myCounter.increment(5);
console.log(myCounter.get());   //-- prints "105"

When we call createCounter(100);, we have the following arrangement:

Notice that createCounter(100) scope is referenced by nested functions increment and get. If createCounter() returned nothing, then, of course, these inner self-references wouldn't be counted, and the scope would be garbage-collected anyway. But since createCounter() returns object containing references to these functions, we have the following:

Take some time to reflect on it: the function createCounter(100) already returned, but its scope is still there, accessible by the inner functions, and only by these functions. It is truly impossible to access createCounter(100) scope object directly, we can only call myCounter.increment() or myCounter.get(). These functions have unique private access to the scope of createCounter.

Let's try to call, for example, myCounter.get(). Recall that when any function is called, new scope object is created, and scope chain that is referenced by the function is augmented with this new scope object. So, when myCounter.get() is called, what we have is:

The first scope object in the chain for function get() is the empty object get() scope. So, when get() accesses counter variable, JavaScript fails to find it on the first object in the scope chain, moves to the next scope object, and uses counter variable on createCounter(100) scope. And get() function just returns it.

You may have noticed that myCounter object is additionally given to the function myCounter.get() as this (denoted by red arrow on the diagram). This is because this is never a part of the scope chain, and you should be aware of it. More on that later.

Calling increment(5) is a bit more interesting, since this function has an argument:

As you see, the argument value is stored in the scope object that was created just for a single call increment(5). When this function accesses variable value, JavaScript immediately locates it on the first object in the scope chain. When, however, the function accesses counter, JavaScript fails to find it on the first object in the scope chain, moves to the next scope object, and locates it there. So, increment() modifies the counter variable on createCounter(100) scope. And virtually nobody else can modify this variable. This is why closures are so powerful: the myCounter object is impossible to compromise. Closures are very appropriate place to store private things.

Notice that the argument initial is also stored in the scope object of createCounter(), even though it is not used. So, we can save a bit of memory if we get rid of explicit var counter = initial;, rename initial to counter, and use it directly. But, for clarity, we have explicit argument initial and var counter.

It is important to highlight that bound scopes are “live”. When function is invoked, current scope chain is not copied for this function: the scope chain is just augmented with new scope object, and when any scope object in the chain is modified by any function, this change is immediately observable by all functions that have this scope object in their scope chains. When increment() modifies counter value, the next call to get() will return updated value.

This is why this well-known example doesn't work:

"use strict";
 
var elems = document.getElementsByClassName("myClass"), i;
 
for (i = 0; i < elems.length; i++) {
  elems[i].addEventListener("click", function () {
    this.innerHTML = i;
  });
}

Multiple functions are created in the loop, and all of them have reference to the same scope object in their scope chains. So, they use exactly the same variable i, not private copies of it. For further explanation of this particular example, see this link: Don't make functions within a loop.

Similar function objects, different scope objects

Now, let's try to expand a bit our counter example and have more fun. What if we create more than one counter objects? It doesn't hurt:

my_script.js
"use strict";
 
function createCounter(initial) {
  /* ... see the code from previous example ... */
}
 
//-- create counter objects
var myCounter1 = createCounter(100);
var myCounter2 = createCounter(200);

When both myCounter1 and myCounter2 are created, we have the following:

Be sure to remember that each function object has a reference to the scope object. So, in the example above, myCounter1.increment and myCounter2.increment refer to function objects that have exactly the same code and the same property values (name, length, and others), but their [[scope]] refer to different scope objects.

Diagram does not contain separate function objects, for the sake of simplicity, but they are still there.

Some examples:

var a, b;
a = myCounter1.get();   // a equals 100
b = myCounter2.get();   // b equals 200
 
myCounter1.increment(1);
myCounter1.increment(2);
 
myCounter2.increment(5);
 
a = myCounter1.get();   // a equals 103
b = myCounter2.get();   // b equals 205

So, this is how it works. The concept of closures is very powerful.

Scope chain and "this"

Like it or not, this is not saved as a part of the scope chain at all. Instead, value of this depends on the function invocation pattern: that is, you may call the same function with different values given as this.

Invocation patterns

This topic pretty much deserves its own article, so I won't go deeply inside, but as a quick overview, there are four invocation patterns. Here we go:

Method invocation pattern

"use strict";
 
var myObj = {
  myProp: 100,
  myFunc: function myFunc() {
    return this.myProp;
  }
};
myObj.myFunc();  //-- returned 100

If an invocation expression contains a refinement (a dot, or [subscript]), the function is invoked as a method. So, in the example above, this given to myFunc() is a reference to myObj.

Function invocation pattern

"use strict";
 
function myFunc() {
  return this;
}
myFunc();   //-- returns undefined

When there's no refinement, then it depends on whether the code runs in strict mode:

Since the code above runs in strict mode thanks to “use strict”;, myFunc() returns undefined.

Constructor invocation pattern

"use strict";
 
function MyObj() {
  this.a = 'a';
  this.b = 'b';
}
var myObj = new MyObj();

When function is called with new prefix, JavaScripts allocates new object which inherits from the function's prototype property, and this newly allocated object is given to the function as this.

Apply invocation pattern

"use strict";
 
function myFunc(myArg) {
  return this.myProp + " " + myArg;
}
 
var result = myFunc.apply(
  { myProp: "prop" },
  [ "arg" ]
);
//-- result is "prop arg"

We can pass arbitrary value as this. In the example above, we use Function.prototype.apply() for that. Beyond that, see also:

In the examples that follow, we will primarily use Method invocation pattern.

Usage of "this" in nested functions

Consider:

"use strict";
 
var myObj = {
 
  myProp: "outer-value",
  createInnerObj: function createInnerObj() {
 
    var hidden = "value-in-closure";
 
    return {
      myProp: "inner-value",
      innerFunc: function innerFunc() {
        return "hidden: '" + hidden + "', myProp: '" + this.myProp + "'";
      }
    };
 
  }
};
 
var myInnerObj = myObj.createInnerObj();
console.log( myInnerObj.innerFunc() );

Prints: hidden: 'value-in-closure', myProp: 'inner-value'

By the time myObj.createInnerObj() is called, we have the following arrangement:

And when we call myInnerObj.innerFunc(), it looks as follows:

From the above, it's clear that this given to myObj.createInnerObj() points to myObj, but this given to myInnerObj.innerFunc() points to myInnerObj: both functions are called with Method invocation pattern, as explained above. That's why this.myProp inside innerFunc() evaluates to “inner-value”, not “outer-value”.

So, we can easily trick innerFunc() into using different myProp, like this:

/* ... see the definition of myObj above ... */
 
var myInnerObj = myObj.createInnerObj();
var fakeObject = {
  myProp: "fake-inner-value",
  innerFunc: myInnerObj.innerFunc
};
console.log( fakeObject.innerFunc() );

Prints: hidden: 'value-in-closure', myProp: 'fake-inner-value'

Or with apply() or call():

/* ... see the definition of myObj above ... */
 
var myInnerObj = myObj.createInnerObj();
console.log(
  myInnerObj.innerFunc.call(
    {
      myProp: "fake-inner-value-2",
    }
  )
);

Prints: hidden: 'value-in-closure', myProp: 'fake-inner-value-2'

Sometimes, however, inner function actually needs access to this given to outer function, independently of the way inner function is invoked. There is a common idiom for that: we need to explicitly save needed value in the closure (that is, in the current scope object), like: var self = this;, and use self in inner function, instead of this. Consider:

"use strict";
 
var myObj = {
 
  myProp: "outer-value",
  createInnerObj: function createInnerObj() {
 
    var self = this;
    var hidden = "value-in-closure";
 
    return {
      myProp: "inner-value",
      innerFunc: function innerFunc() {
        return "hidden: '" + hidden + "', myProp: '" + self.myProp + "'";
      }
    };
 
  }
};
 
var myInnerObj = myObj.createInnerObj();
console.log( myInnerObj.innerFunc() );

Prints: hidden: 'value-in-closure', myProp: 'outer-value'

This way, we have the following:

As you see, this time, innerFunc() has access to the value given as this to the outer function, through the self stored in the closure.

Conclusion

Let's answer a couple of questions from the beginning of the article:

Further reading:

Discuss on reddit: or on Hacker News: Vote on HN