Event Capture and Bubbling

The DOM has two ways for objects to detect events: from the top down, and from the bottom up. The first method is known as event capture, the second is called event bubbling.

Event Capture

diagram showing arrows moving downwards

Let’s say that your document contains a<div>which contains a<p>which contains an<img>. Further, let’s say you’ve added an event listener to all of them. When a user clicks on the image, amouseclickevent occurs.

Even though the user clicked the image, the image doesn’t get the event first. Instead, the event listener attached to the document grabs the event first and processes it. (That is, it captures the event before it gets to its intended target.) The event is then passed down to the <div>’s event listener. The event then goes to the <p>, and finally to the <img>. That is, all of the clicked-on object’s “ancestors” higher up in the document capture the event for processing before sending it down the chain to its intended target. You can try event capture in a pop-up window. (This only works in Mozilla.)

Event Bubbling

diagram showing arrows moving upwards

Now let’s look at the same situation from the inside out. You have an <img>inside a <p>, which is inside a <div>, which is inside your document. When a user clicks the image, this time the events rise like a bubble in a glass of water. The click’s original target, the <img>, gets to see the event first, and then passes it upwards to the <p>for further processing, which passes it on to the <div>, which finally passes it up to the document. You can try event bubbling in a pop-up window. (This only works in Mozilla.)

Here are the relevant parts of the source code for both demos.

http://javascript.info/tutorial/bubbling-and-capturing

에서 참조

Bubbling and capturing

Ilya Kantor

  1. Bubbling
    1. this and event.target
    2. Stopping the bubbling
  2. Capturing
  3. Summary

DOM elements can be nested inside each other. And somehow, the handler of the parent works even if you click on it’s child.

The reason is event bubbling.

For example, the following DIV handler runs even if you click a nested tag like EM or CODE:

<div onclick="alert('Div handler worked!')">
  <em>Click here triggers on nested <code>EM</code>, not on <code>DIV</code></em>
</div>

That’s because an event bubbles from the nested tag up and triggers the parent.

Bubbling

The main principle of bubbling states:
After an event triggers on the deepest possible element, it then triggers on parents in nesting order.

For example, there are 3 nested divs:

01 <!DOCTYPE HTML>
02 <html>
03 <body>
04 <link type="text/css" rel="stylesheet" href="example.css">
05 
06 <div class="d1">1  <!-- the topmost -->
07     <div class="d2">2
08         <div class="d3">3 <!-- the innermost -->
09         </div>
10     </div>
11 </div>
12 
13 </body>
14 </html>

The bubbling guarantees that click on Div 3 will trigger onclick first on the innermost element 3 (also caled the target), then on the element 2, and the last will be element 1.

Bubbling events order

The order is called a bubbling order, because an event bubbles from the innermost element up through parents, like a bubble of air in the water.

Click below to see it bubble:

Open the code in new window

this and event.target

The deepest element which triggered the event is called the target or, the originating element.

Internet Explorer has the srcElement property for it, all W3C-compliant browsers use event.target. The cross-browser code is usually like this:

var target = event.target || event.srcElement

When handlers trigger on parents:

  • event.target/srcElement – remains the same originating element.
  • this – is the current element, the one event has bubbled to, the one which runs the handler.

Bubbling events order

In the example below, each DIV has an onclick handler which outputs both target and this.

Click on a div.

Note that:

  • the target is constant through all bubbling process,
  • this changes and gets highlighted.

Open the code in new window

In W3C-compliant browsers this is also available as event.currentTarget.

attachEvent does not pass this or event.currentTarget at all.

Stopping the bubbling

The bubbling goes right to the top. When an event occurs on an element – it will bubble up to <HTML>, triggering handlers on it’s way.

But a handler may decide that event is fully processed and stop the bubbling.

The code is:

  • For W3C-compliant browsers:
    event.stopPropagation()
  • For IE<9:
    event.cancelBubble = true

The cross-browser-code:

01 element.onclick = function(event) {
02     event = event || window.event // cross-browser event
03      
04     if (event.stopPropagation) {
05         // W3C standard variant
06         event.stopPropagation()
07     } else {
08         // IE variant
09         event.cancelBubble = true
10     }
11 }

There is a one-lined variant too:

event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true)

If the element has several handlers on same event, then handlers are independent. All of them get executed..

For example, if there are two onclick handlers on the same link, then stopping bubbling in one of them has no effect on the other one. Also, the browser doesn’t guarantee the order in which they trigger.

Capturing

In all browsers, except IE<9, there are two stages of event processing.

The event first goes down – that’s called capturing, and then bubbles up. This behavior is standartized in W3C specification.

W3C events order

According to this model, the event:

  1. Captures down – through 1 -> 2 -> 3.
  2. Bubbles up – through 3 -> 2 -> 1.

All methods of event handling ignore the caputiring phase. Using addEventListener with last argument true is only the way to catch the event at capturing.

elem.addEventListener( type, handler, phase )

phase = true The handler is set on the capturing phase. phase = falseThe handler is set on the bubbling phase.

Click in a div below to see capturing in action (no IE<9):

It should be 1 -> 2 -> 3.

Source JavaScript of the example:

1 var divs = document.getElementsByTagName('div')
2 
3 for(var i=0; i<divs.length; i++) {
4   divs[i].addEventListener("click", highlightThis, true)
5 }

Click to open in the playground: tutorial/browser/events/bubbling/capture/index.html.

In real-life the capturing phase is rarely used. But..
There are events which don’t bubble, but can be captured. For example, onfocus/onblur.

Now let’s assign handlers at both stages.

Click on a div below to see the event processing order (no IE<9):

It should be 1 -> 2 -> 3 -> 3 -> 2 -> 1.

Source JavaScript of the example:

1 var divs = document.getElementsByTagName('div')
2 
3 for(var i=0; i<divs.length; i++) {
4   divs[i].addEventListener("click", highlightThis, true)
5   divs[i].addEventListener("click", highlightThis, false)
6 }

Click to open in the playground: tutorial/browser/events/bubbling/both/index.html.

Summary

  • Events first are captured down to deepest target, then bubble up. In IE<9 they only bubble.
  • All handlers work on bubbling stage excepts addEventListener with last argument true, which is the only way to catch the event on capturing stage.
  • Bubbling/capturing can be stopped by event.cancelBubble=true (IE) or event.stopPropagation() for other browsers.

http://javascript.info/tutorial/bubbling-and-capturing

에서 참조

Bubbling and capturing

Ilya Kantor

  1. Bubbling
    1. this and event.target
    2. Stopping the bubbling
  2. Capturing
  3. Summary

DOM elements can be nested inside each other. And somehow, the handler of the parent works even if you click on it’s child.

The reason is event bubbling.

For example, the following DIV handler runs even if you click a nested tag like EM or CODE:

<div onclick="alert('Div handler worked!')">
  <em>Click here triggers on nested <code>EM</code>, not on <code>DIV</code></em>
</div>

That’s because an event bubbles from the nested tag up and triggers the parent.

Bubbling

The main principle of bubbling states:
After an event triggers on the deepest possible element, it then triggers on parents in nesting order.

For example, there are 3 nested divs:

01 <!DOCTYPE HTML>
02 <html>
03 <body>
04 <link type="text/css" rel="stylesheet" href="example.css">
05 
06 <div class="d1">1  <!-- the topmost -->
07     <div class="d2">2
08         <div class="d3">3 <!-- the innermost -->
09         </div>
10     </div>
11 </div>
12 
13 </body>
14 </html>

The bubbling guarantees that click on Div 3 will trigger onclick first on the innermost element 3 (also caled the target), then on the element 2, and the last will be element 1.

Bubbling events order

The order is called a bubbling order, because an event bubbles from the innermost element up through parents, like a bubble of air in the water.

Click below to see it bubble:

Open the code in new window

this and event.target

The deepest element which triggered the event is called the target or, the originating element.

Internet Explorer has the srcElement property for it, all W3C-compliant browsers use event.target. The cross-browser code is usually like this:

var target = event.target || event.srcElement

When handlers trigger on parents:

  • event.target/srcElement – remains the same originating element.
  • this – is the current element, the one event has bubbled to, the one which runs the handler.

Bubbling events order

In the example below, each DIV has an onclick handler which outputs both target and this.

Click on a div.

Note that:

  • the target is constant through all bubbling process,
  • this changes and gets highlighted.

Open the code in new window

In W3C-compliant browsers this is also available as event.currentTarget.

attachEvent does not pass this or event.currentTarget at all.

Stopping the bubbling

The bubbling goes right to the top. When an event occurs on an element – it will bubble up to <HTML>, triggering handlers on it’s way.

But a handler may decide that event is fully processed and stop the bubbling.

The code is:

  • For W3C-compliant browsers:
    event.stopPropagation()
  • For IE<9:
    event.cancelBubble = true

The cross-browser-code:

01 element.onclick = function(event) {
02     event = event || window.event // cross-browser event
03      
04     if (event.stopPropagation) {
05         // W3C standard variant
06         event.stopPropagation()
07     } else {
08         // IE variant
09         event.cancelBubble = true
10     }
11 }

There is a one-lined variant too:

event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true)

If the element has several handlers on same event, then handlers are independent. All of them get executed..

For example, if there are two onclick handlers on the same link, then stopping bubbling in one of them has no effect on the other one. Also, the browser doesn’t guarantee the order in which they trigger.

Capturing

In all browsers, except IE<9, there are two stages of event processing.

The event first goes down – that’s called capturing, and then bubbles up. This behavior is standartized in W3C specification.

W3C events order

According to this model, the event:

  1. Captures down – through 1 -> 2 -> 3.
  2. Bubbles up – through 3 -> 2 -> 1.

All methods of event handling ignore the caputiring phase. Using addEventListener with last argument true is only the way to catch the event at capturing.

elem.addEventListener( type, handler, phase )

phase = true The handler is set on the capturing phase. phase = falseThe handler is set on the bubbling phase.

Click in a div below to see capturing in action (no IE<9):

It should be 1 -> 2 -> 3.

Source JavaScript of the example:

1 var divs = document.getElementsByTagName('div')
2 
3 for(var i=0; i<divs.length; i++) {
4   divs[i].addEventListener("click", highlightThis, true)
5 }

Click to open in the playground: tutorial/browser/events/bubbling/capture/index.html.

In real-life the capturing phase is rarely used. But..
There are events which don’t bubble, but can be captured. For example, onfocus/onblur.

Now let’s assign handlers at both stages.

Click on a div below to see the event processing order (no IE<9):

It should be 1 -> 2 -> 3 -> 3 -> 2 -> 1.

Source JavaScript of the example:

1 var divs = document.getElementsByTagName('div')
2 
3 for(var i=0; i<divs.length; i++) {
4   divs[i].addEventListener("click", highlightThis, true)
5   divs[i].addEventListener("click", highlightThis, false)
6 }

Click to open in the playground: tutorial/browser/events/bubbling/both/index.html.

Summary

  • Events first are captured down to deepest target, then bubble up. In IE<9 they only bubble.
  • All handlers work on bubbling stage excepts addEventListener with last argument true, which is the only way to catch the event on capturing stage.
  • Bubbling/capturing can be stopped by event.cancelBubble=true (IE) or event.stopPropagation() for other browsers.

http://stackoverflow.com/questions/111102/how-do-javascript-closures-work

에서 참조

JavaScript Closures For Dummies (mirror) is the article that finally got me to

understand closures. The explanation posted there is much better than anything I could write here.


For archiving purposes, I (flying sheep) will put the article from the link below. The article was created by Morris and put under the Creative Commons Attribution / Share alike license, so I’ll recreate it as close to the original as possible.

JavaScript Closures for Dummies

Submitted by Morris on Tue, 2006-02-21 10:19.

Closures Are Not Magic

This page explains closures so that a programmer can understand them — using working JavaScript code. It is not for gurus or functional programmers.

Closures are not hard to understand once the core concept is grokked. However, they are impossible to understand by reading any academic papers or academically oriented information about them!

This article is intended for programmers with some programming experience in a mainstream language, and who can read the following JavaScript function:

function sayHello(name) { var text = 'Hello ' + name; var sayAlert = function() { alert(text); } sayAlert(); }

An Example of a Closure

Two one sentence summaries:

  • a closure is the local variables for a function — kept alive after the function has returned, or
  • a closure is a stack-frame which is not deallocated when the function returns (as if a ‘stack-frame’ were malloc’ed instead of being on the stack!).

The following code returns a reference to a function:

function sayHello2(name) { var text = 'Hello ' + name; // Local variable var sayAlert = function() { alert(text); } return sayAlert; } say2 = sayHello2('Bob'); say2(); // alerts "Hello Bob"

Most JavaScript programmers will understand how a reference to a function is returned to a variable in the above code. If you don’t, then you need to before you can learn closures. A C programmer would think of the function as returning a pointer to a function, and that the variables sayAlert and say2 were each a pointer to a function.

There is a critical difference between a C pointer to a function and a JavaScript reference to a function. In JavaScript, you can think of a function reference variable as having both a pointer to a function as well as a hidden pointer to a closure.

The above code has a closure because the anonymous function function() { alert(text); } is declared inside another function, sayHello2() in this example. In JavaScript, if you use the function keyword inside another function, you are creating a closure.

In C, and most other common languages after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed.

In JavaScript, if you declare a function within another function, then the local variables can remain accessible after returning from the function you called. This is demonstrated above, because we call the function say2() after we have returned from sayHello2(). Notice that the code that we call references the variable text, which was a local variable of the function sayHello2().

function() { alert(text); } // Output of say2.toString();

Click the button above to get JavaScript to print out the code for the anonymous function. You can see that the code refers to the variable text. The anonymous function can reference text which holds the value 'Jane' because the local variables of sayHello2() are kept in a closure.

The magic is that in JavaScript a function reference also has a secret reference to the closure it was created in — similar to how delegates are a method pointer plus a secret reference to an object.

More examples

For some reason, closures seem really hard to understand when you read about them, but when you see some examples you can click to how they work (it took me a while). I recommend working through the examples carefully until you understand how they work. If you start using closures without fully understanding how they work, you would soon create some very weird bugs!

Example 3

This example shows that the local variables are not copied — they are kept by reference. It is kind of like keeping a stack-frame in memory when the outer function exits!

function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; }

Example 4

All three global functions have a common reference to the same closure because they are all declared within a single call to setupSomeGlobals().

function setupSomeGlobals() { // Local variable that ends up within closure var num = 666; // Store some references to functions as global variables gAlertNumber = function() { alert(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } }

The three functions have shared access to the same closure — the local variables of setupSomeGlobals() when the three functions were defined.

Note that in the above example, if you click setupSomeGlobals() again, then a new closure (stack-frame!) is created. The old gAlertNumber, gIncreaseNumber, gSetNumber variables are overwritten with new functions that have the new closure. (In JavaScript, whenever you declare a function inside another function, the inside function(s) is/are recreated again each time the outside function is called.)

Example 5

This one is a real gotcha for many people, so you need to understand it. Be very careful if you are defining a function within a loop: the local variables from the closure do not act as you might first think.

function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( function() {alert(item + ' ' + list[i])} ); } return result; } function testList() { var fnlist = buildList([1,2,3]); // Using j only to help prevent confusion -- could use i. for (var j = 0; j < fnlist.length; j++) { fnlist[j](); } }

The line result.push( function() {alert(item + ' ' + list[i])} adds a reference to an anonymous function three times to the result array. If you are not so familiar with anonymous functions think of it like:

pointer = function() {alert(item + ' ' + list[i])}; result.push(pointer);

Note that when you run the example, "item3 undefined" is alerted three times! This is because just like previous examples, there is only one closure for the local variables for buildList. When the anonymous functions are called on the line fnlist[j](); they all use the same single closure, and they use the current value for i and item within that one closure (where i has a value of 3 because the loop had completed, and item has a value of 'item3').

Example 6

This example shows that the closure contains any local variables that were declared inside the outer function before it exited. Note that the variable alice is actually declared after the anonymous function. The anonymous function is declared first; and when that function is called it can access the alice variable because alice is in the closure. Also sayAlice()() just directly calls the function reference returned from sayAlice() — it is exactly the same as what was done previously, but without the temporary variable.

function sayAlice() { var sayAlert = function() { alert(alice); } // Local variable that ends up within closure var alice = 'Hello Alice'; return sayAlert; } sayAlice()();

Tricky: note also that the sayAlert variable is also inside the closure, and could be accessed by any other function that might be declared within sayAlice(), or it could be accessed recursively within the inside function.

Example 7

This final example shows that each call creates a separate closure for the local variables. There is not a single closure per function declaration. There is a closure for each call to a function.

function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); alert('num: ' + num + 'nanArray ' + anArray.toString() + 'nref.someVar ' + ref.someVar); } } obj = {someVar: 4}; fn1 = newClosure(4, obj); fn2 = newClosure(5, obj); fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4; fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4; obj.someVar++; fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5; fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Summary

If everything seems completely unclear then the best thing to do is to play with the examples. Reading an explanation is much harder than understanding examples. My explanations of closures and stack-frames, etc. are not technically correct — they are gross simplifications intended to help understanding. Once the basic idea is grokked, you can pick up the details later.

Final points:

  • Whenever you use function inside another function, a closure is used.
  • Whenever you use eval() inside a function, a closure is used. The text you eval can reference local variables of the function, and within eval you can even create new local variables by using eval('var foo = …')
  • When you use new Function(…) (the Function constructor) inside a function, it does not create a closure. (The new function cannot reference the local variables of the outer function.)
  • A closure in JavaScript is like keeping a copy of all the local variables, just as they were when a function exited.
  • It is probably best to think that a closure is always created just on entry to a function, and the local variables are added to that closure.
  • A new set of local variables is kept every time a function with a closure is called (given that the function contains a function declaration inside it, and a reference to that inside function is either returned or an external reference is kept for it in some way).
  • Two functions might look like they have the same source text, but have completely different behaviour because of their ‘hidden’ closure. I don’t think JavaScript code can actually find out if a function reference has a closure or not.
  • If you are trying to do any dynamic source code modifications (for example: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), it won’t work if myFunction is a closure (of course, you would never even think of doing source code string substitution at runtime, but…).
  • It is possible to get function declarations within function declarations within functions — and you can get closures at more than one level.
  • I think normally a closure is the term for both the function along with the variables that are captured. Note that I do not use that definition in this article!
  • I suspect that closures in JavaScript differ from those normally found in functional languages.

Links

Thanks

If you have just learnt closures (here or elsewhere!), then I am interested in any feedback from you about any changes you might suggest that could make this article clearer. Send an email to morrisjohns.com (morris_closure @). Please note that I am not a guru on JavaScript — nor on closures.

Thanks for reading

http://stackoverflow.com/questions/111102/how-do-javascript-closures-work

에서 참조

JavaScript Closures For Dummies (mirror) is the article that finally got me to

understand closures. The explanation posted there is much better than anything I could write here.


For archiving purposes, I (flying sheep) will put the article from the link below. The article was created by Morris and put under the Creative Commons Attribution / Share alike license, so I’ll recreate it as close to the original as possible.

JavaScript Closures for Dummies

Submitted by Morris on Tue, 2006-02-21 10:19.

Closures Are Not Magic

This page explains closures so that a programmer can understand them — using working JavaScript code. It is not for gurus or functional programmers.

Closures are not hard to understand once the core concept is grokked. However, they are impossible to understand by reading any academic papers or academically oriented information about them!

This article is intended for programmers with some programming experience in a mainstream language, and who can read the following JavaScript function:

function sayHello(name) { var text = 'Hello ' + name; var sayAlert = function() { alert(text); } sayAlert(); }

An Example of a Closure

Two one sentence summaries:

  • a closure is the local variables for a function — kept alive after the function has returned, or
  • a closure is a stack-frame which is not deallocated when the function returns (as if a ‘stack-frame’ were malloc’ed instead of being on the stack!).

The following code returns a reference to a function:

function sayHello2(name) { var text = 'Hello ' + name; // Local variable var sayAlert = function() { alert(text); } return sayAlert; } say2 = sayHello2('Bob'); say2(); // alerts "Hello Bob"

Most JavaScript programmers will understand how a reference to a function is returned to a variable in the above code. If you don’t, then you need to before you can learn closures. A C programmer would think of the function as returning a pointer to a function, and that the variables sayAlert and say2 were each a pointer to a function.

There is a critical difference between a C pointer to a function and a JavaScript reference to a function. In JavaScript, you can think of a function reference variable as having both a pointer to a function as well as a hidden pointer to a closure.

The above code has a closure because the anonymous function function() { alert(text); } is declared inside another function, sayHello2() in this example. In JavaScript, if you use the function keyword inside another function, you are creating a closure.

In C, and most other common languages after a function returns, all the local variables are no longer accessible because the stack-frame is destroyed.

In JavaScript, if you declare a function within another function, then the local variables can remain accessible after returning from the function you called. This is demonstrated above, because we call the function say2() after we have returned from sayHello2(). Notice that the code that we call references the variable text, which was a local variable of the function sayHello2().

function() { alert(text); } // Output of say2.toString();

Click the button above to get JavaScript to print out the code for the anonymous function. You can see that the code refers to the variable text. The anonymous function can reference text which holds the value 'Jane' because the local variables of sayHello2() are kept in a closure.

The magic is that in JavaScript a function reference also has a secret reference to the closure it was created in — similar to how delegates are a method pointer plus a secret reference to an object.

More examples

For some reason, closures seem really hard to understand when you read about them, but when you see some examples you can click to how they work (it took me a while). I recommend working through the examples carefully until you understand how they work. If you start using closures without fully understanding how they work, you would soon create some very weird bugs!

Example 3

This example shows that the local variables are not copied — they are kept by reference. It is kind of like keeping a stack-frame in memory when the outer function exits!

function say667() { // Local variable that ends up within closure var num = 666; var sayAlert = function() { alert(num); } num++; return sayAlert; }

Example 4

All three global functions have a common reference to the same closure because they are all declared within a single call to setupSomeGlobals().

function setupSomeGlobals() { // Local variable that ends up within closure var num = 666; // Store some references to functions as global variables gAlertNumber = function() { alert(num); } gIncreaseNumber = function() { num++; } gSetNumber = function(x) { num = x; } }

The three functions have shared access to the same closure — the local variables of setupSomeGlobals() when the three functions were defined.

Note that in the above example, if you click setupSomeGlobals() again, then a new closure (stack-frame!) is created. The old gAlertNumber, gIncreaseNumber, gSetNumber variables are overwritten with new functions that have the new closure. (In JavaScript, whenever you declare a function inside another function, the inside function(s) is/are recreated again each time the outside function is called.)

Example 5

This one is a real gotcha for many people, so you need to understand it. Be very careful if you are defining a function within a loop: the local variables from the closure do not act as you might first think.

function buildList(list) { var result = []; for (var i = 0; i < list.length; i++) { var item = 'item' + list[i]; result.push( function() {alert(item + ' ' + list[i])} ); } return result; } function testList() { var fnlist = buildList([1,2,3]); // Using j only to help prevent confusion -- could use i. for (var j = 0; j < fnlist.length; j++) { fnlist[j](); } }

The line result.push( function() {alert(item + ' ' + list[i])} adds a reference to an anonymous function three times to the result array. If you are not so familiar with anonymous functions think of it like:

pointer = function() {alert(item + ' ' + list[i])}; result.push(pointer);

Note that when you run the example, "item3 undefined" is alerted three times! This is because just like previous examples, there is only one closure for the local variables for buildList. When the anonymous functions are called on the line fnlist[j](); they all use the same single closure, and they use the current value for i and item within that one closure (where i has a value of 3 because the loop had completed, and item has a value of 'item3').

Example 6

This example shows that the closure contains any local variables that were declared inside the outer function before it exited. Note that the variable alice is actually declared after the anonymous function. The anonymous function is declared first; and when that function is called it can access the alice variable because alice is in the closure. Also sayAlice()() just directly calls the function reference returned from sayAlice() — it is exactly the same as what was done previously, but without the temporary variable.

function sayAlice() { var sayAlert = function() { alert(alice); } // Local variable that ends up within closure var alice = 'Hello Alice'; return sayAlert; } sayAlice()();

Tricky: note also that the sayAlert variable is also inside the closure, and could be accessed by any other function that might be declared within sayAlice(), or it could be accessed recursively within the inside function.

Example 7

This final example shows that each call creates a separate closure for the local variables. There is not a single closure per function declaration. There is a closure for each call to a function.

function newClosure(someNum, someRef) { // Local variables that end up within closure var num = someNum; var anArray = [1,2,3]; var ref = someRef; return function(x) { num += x; anArray.push(num); alert('num: ' + num + 'nanArray ' + anArray.toString() + 'nref.someVar ' + ref.someVar); } } obj = {someVar: 4}; fn1 = newClosure(4, obj); fn2 = newClosure(5, obj); fn1(1); // num: 5; anArray: 1,2,3,5; ref.someVar: 4; fn2(1); // num: 6; anArray: 1,2,3,6; ref.someVar: 4; obj.someVar++; fn1(2); // num: 7; anArray: 1,2,3,5,7; ref.someVar: 5; fn2(2); // num: 8; anArray: 1,2,3,6,8; ref.someVar: 5;

Summary

If everything seems completely unclear then the best thing to do is to play with the examples. Reading an explanation is much harder than understanding examples. My explanations of closures and stack-frames, etc. are not technically correct — they are gross simplifications intended to help understanding. Once the basic idea is grokked, you can pick up the details later.

Final points:

  • Whenever you use function inside another function, a closure is used.
  • Whenever you use eval() inside a function, a closure is used. The text you eval can reference local variables of the function, and within eval you can even create new local variables by using eval('var foo = …')
  • When you use new Function(…) (the Function constructor) inside a function, it does not create a closure. (The new function cannot reference the local variables of the outer function.)
  • A closure in JavaScript is like keeping a copy of all the local variables, just as they were when a function exited.
  • It is probably best to think that a closure is always created just on entry to a function, and the local variables are added to that closure.
  • A new set of local variables is kept every time a function with a closure is called (given that the function contains a function declaration inside it, and a reference to that inside function is either returned or an external reference is kept for it in some way).
  • Two functions might look like they have the same source text, but have completely different behaviour because of their ‘hidden’ closure. I don’t think JavaScript code can actually find out if a function reference has a closure or not.
  • If you are trying to do any dynamic source code modifications (for example: myFunction = Function(myFunction.toString().replace(/Hello/,'Hola'));), it won’t work if myFunction is a closure (of course, you would never even think of doing source code string substitution at runtime, but…).
  • It is possible to get function declarations within function declarations within functions — and you can get closures at more than one level.
  • I think normally a closure is the term for both the function along with the variables that are captured. Note that I do not use that definition in this article!
  • I suspect that closures in JavaScript differ from those normally found in functional languages.

Links

Thanks

If you have just learnt closures (here or elsewhere!), then I am interested in any feedback from you about any changes you might suggest that could make this article clearer. Send an email to morrisjohns.com (morris_closure @). Please note that I am not a guru on JavaScript — nor on closures.

Thanks for reading