How do 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:

  • in strict mode, this is undefined
  • in non-strict mode, this points to Global Object

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:

  • What the closure is? - It is the object that refers both to function object and the scope object. Actually, all JavaScript functions are closures: it's impossible to have the reference to function object without scope object.
  • When is it created? - Since all JavaScript functions are closures, it's obvious: when you define a function, you actually define a closure. So, it is created when the function is defined. But make sure you distinguish between closure creation and new scope object creation: the closure (function + reference to the current scope chain) is created when the function is defined, but new scope object is created (and used to augment the closure's scope chain) when the function is invoked.
  • When is it deleted? - Just like any regular object in JavaScript, it is garbage-collected when there are no references to it.

Further reading:

  • JavaScript: The Good Parts by Douglas Crockford. It's surely nice to understand how do closures work, but it's probably even more important to understand how to use them correctly. The book is very concise, and it contains lots of great and useful patterns.
  • JavaScript: The Definitive Guide by David Flanagan. As the title suggests, this book explains the language in a very detailed way.

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

Discussion

George, 2015/09/06 00:05

Thanks for the great article. Really helpful for me.

A little question: what software do you use to draw these diagrams?

Dmitry Frank, 2015/09/06 00:18

Glad it helped.

As to diagram-drawing software, it's yEd. Not that I'm completely satisfied with it, but it's the best of what I've came up with. Take a look at PlantUML as well, I use it sometimes and I really like it for its text sources. (and if it had supported links to specific member of the object, not just to the whole object, I would've used PlantUML for this article as well)

Andrew J Dittman, 2015/09/06 14:44

The best explanation of closures I've read. Thank you.

Dmitry Frank, 2015/09/06 15:51

You're welcome, Andrew!

Scott, 2015/09/06 16:11

Thanks for this post, it's very interesting. High Performance JavaScript by Nicholas C. Zakas has some great examples and illustrations in Chapter 2, have you checked those out?

Dmitry Frank, 2015/09/06 16:17

Scott, I have this book on my to-read list, but haven't read it yet. So will probably get to it when I finish The Definitive Guide. Thanks for mentioning it!

Patrick, 2015/09/06 17:00

Good article. It would be interesting to add examples if you take a function that has closure and use function.prototype.call() to provide a this and show that may change the scope chain.

Dmitry Frank, 2015/09/06 22:00

Hi Patrick, thanks for your feedback.

Regarding to Function.prototype.call(), it's a bit misleading to say that providing custom this will change the scope chain: actually, this is not saved as part of the scope chain at all.

Thanks for the suggestion, this topic is definitely worth explaining, so, I've added new section in the article: Scope chain and "this".

Priyank, 2015/09/06 19:14

Hey, Really good Article . But i have a question let's say the increment function inside has some locally defined variables so when we call myCounter.increment() then what is the scope of that variables after function execution will they be garbage collected ?

Dmitry Frank, 2015/09/06 19:28

Hi Priyank, thanks for your feedback.

When we call myCounter.increment(), we have the scope chain as in this diagram, just replace get with increment.

So, if increment() has some local variables, like this:

  function increment() {
    var myLocalVar = 10; //-- local variable (though useless here)
    counter++;
  }

Then, variable myLocalVar will be stored in the left scope (which is empty in the diagram). And yes, when increment() returns, the scope with myLocalVar will be garbage-collected.

Note that particular implementations of JavaScript may optimize empty scopes away (as well as scopes which contain only unused variables), but the idea is still here.

Dmitry Frank, 2015/09/07 03:51

Priyank, I've updated the article: now, for clarity, the function increment() takes an argument. So, the diagram now looks as follows, and you might want to re-read the section Nested functions.

Thanks for your question, it helped me to understand what should be improved.

Sharat M R, 2015/09/06 19:21

Thank you for such a great article on JS Closures. One clarification - Won't myCounter1.increment and myCounter2.increment refer to different function objects as the function increment is defined when createCounter is called?

Dmitry Frank, 2015/09/06 19:41

Hello Sharat! Thank you very much for your comment. Although I did my best to re-check myself before publishing, I finally made a mistake. :( Yes, you're right; of course, the function objects, while having exactly the same code, will be two different objects. How could I miss that..

Thanks again, I will fix the article.

Dillon de Voor, 2015/09/07 08:03

Loved reading this article, thanks! However this part confuses me:

  “the myFunc variable not only refers to the function object, but also to the scope object”

How can a single reference references two objects? According to the specification (step 9) scope is stored on the function object itself, so this diagram makes more sense:

                      +---------------+
  +---------------+   |function object|
  | Global object |<----[[scope]]     |
  |---------------+   |               |
  | myFun = <func>--->|               |
  +---------------+   +---------------+

What do you think?

Dmitry Frank, 2015/09/07 08:31

Thank you very much for your valuable feedback!

Well, my understanding was that there might be some intermediary object that references function object and scope object, but now it's obvious that I was wrong in that: of course, the function object itself references scope object. Thanks for pointing this out.

Dillon de Voor, 2015/09/10 17:58

No problem, really great article! Got me thinking and digging in the spec.

Also the difference between outer and inner scope kept me busy for a while. I believe the outer scope is the scope in which the function is defined (referenced as [[scope]]) and the inner scope is a new scope created every time the function is called (meaning there can be many inner scopes).

Ian Warburton, 2015/09/09 17:26

Great article.

So the function object's reference to its scope is used to construct the scope chain?

So in your third image, the [[scope]] pointer is copied to create the left most white-headed arrow?

Dmitry Frank, 2015/09/09 17:57

Thank you for your feedback.

So the function object's reference to its scope is used to construct the scope chain?

Right.

So in your third image, the [[scope]] pointer is copied to create the left most white-headed arrow?

Yes, we can see it in this way. In UML, the white-headed arrow is used to denote the inheritance, so, that's what I tried to show. So, when the function is called, new scope object is created (the leftmost one on the diagram), and it inherits from the scope object pointed to by function's [[scope]].

Rolograaf , 2015/09/12 20:49

very visually helpful looking forward to follow up article with ES6 let and arrow functions?

Dmitry Frank, 2015/09/14 19:53

Rolograaf, thank for the feedback. Sorry for the late reply, I was outdoors for several days.

I was already thinking of expanding the article with let from ES6. I hope I'll have some time for it. As to arrow functions, as far as I know, they're just a syntactic sugar (even though an appealing one), so, they don't bring anything new to the “under the hood” picture of scopes.

Chad Walker, 2015/09/14 15:01

Hi, I enjoyed the article. I think you should add initial to the createCounter() scopes. I was slightly confused when you had value in the increment scope. I hadn't realized at first that arguments would be in the scope; but of course they would, initial is still accessible to get() and increment() even though it's not used.

Dmitry Frank, 2015/09/14 19:45

Hi Chad, I'm glad you enjoyed!

And yes, you're right; I've added initial to diagrams. Thanks for pointing this out.

Mohamed Hussain S H, 2015/09/15 13:48

Thanks for the clear and nice explanation. Special thanks for including the “This” also in the article. The article clearly explains two most misunderstood concepts this and closures are not related to one other.

Dmitry Frank, 2015/09/15 13:53

You're welcome! It's always good to get some positive feedback, thanks!

evandrix, 2015/09/16 06:00

It's “clarity”, not “clearness”.

Dmitry Frank, 2015/09/16 11:28

Ok, thanks. Noted. I'm wondering whether it is the most critical English mistake I've made (and even not in the article, but in comment)

Charlie301, 2015/09/24 18:50

Dude much appreciated for the article. Charlie

Dmitry Frank, 2015/09/24 19:12

You're welcome, Charlie! And thanks for your kind feedback.

Parambir Singh, 2015/09/28 04:47

Great article. I finally understand how closures work in JavaScript and also what 'this' means. Thanks a lot for your effort :)

Dmitry Frank, 2015/09/28 08:17

You're welcome. Glad it helped :)

Francis Kim, 2015/10/07 04:54

What an excellent write up. I'll need to revisit to read it again! :)

Dmitry Frank, 2015/10/10 07:55

Thanks!

Leon LI, 2015/10/10 03:21

This article is super!

I thought i totally understand the lexical scope stuff. After reading this, I found I actually had many missing corners.

Thanks for sharing.

Dmitry Frank, 2015/10/10 07:55

You're welcome, Leon, glad you liked!

Duc Nghiem Xuan, 2015/11/25 06:17

Hello, thank you for a very useful article.

I've translate it to Vietnamese at https://viblo.asia/xuanduc987/posts/lA7GKnWWMKZQ

Dmitry Frank, 2015/11/25 08:35

Hello, thanks, that's great! I updated the list at the top.

李彦峰, 2016/10/07 06:31

Great article!非常棒,感谢博主!

Dmitry Frank, 2016/10/07 07:29

Thanks for the comment :)

Enter your comment (please, English only). Wiki syntax is allowed:
If you can't read the letters on the image, download this .wav file to get them read to you.
 
articles/js_closures.txt · Last modified: 2015/11/25 08:34 by dfrank
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0