Sergio Pereira

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
JavaScript, time to grok closures
This post is part of a series called JavaScript Demystified.

When I wrote about functions in JavaScript I mentioned that functions are more than just a block of code:

Function is a standard data type in JavaScript, an object indeed; you can pass them around and copy them.

Let's take a look at this small sample of a function that creates and returns another function. The returned function accepts one string argument and returns another string repeating the argument a number of times.

function makeRepeater(times){
	return function(text){
		var message = '';
		for (var i=0; i < times; i++) {
			message += text + ' ';
		}
		return message;
	};
}

Let's now write some code that uses that function.

var threeTimes = makeRepeater(3);
var fourTimes = makeRepeater(4);
alert( threeTimes('hi') );
// => 'hi hi hi '
alert( fourTimes('hi') );
// => 'hi hi hi hi '

Nothing spectacular, right? But look closely. The function returned by makeRepeater contains a reference to times, which is a local variable of makeRepeater. When we call threeTimes or fourTimes the makeRepeater call has already returned and times should be out of scope. Or should it?

Extra life for your local scope

You may try to argue and say that the times inside threeTimes is not a reference to the times from makeRepeater, but just a copy of that value. Well, sadly I'll have to prove you wrong. Let's modify our code just a little.

var times;
function makeRepeater(){
	return function(text){
		var message = '';
		for (var i=0; i < times; i++) {
			message += text + ' ';
		}
		return message;
	};
}

times = 3;
var threeTimes = makeRepeater();
times = 4;
var fourTimes = makeRepeater();
alert( threeTimes('hi') );
// => 'hi hi hi hi '  ---> What?!?!
alert( fourTimes('hi') );
// => 'hi hi hi hi '

If it's not clear yet, let me write it down for you. The returned function really keeps a reference to any outside values it will need when invoked. In our original example, it kept a reference to the times local variable at the time it was produced. If we had created other local variables inside makeRepeater they would also become available inside the returned function. In other words, all the scope created during the call to makeRepeater will be preserved for the returned function. This happens when the returned (or inner) function has a reference to anything defined in the parent (or outer) function, i.e. the parent local scope. When this happens, we say that a closure has been created.

Closures can be tricky

It's important to understand the mechanics of closures to avoid subtle bugs in our code. Look at this piece of code, adapted from a real bug I had to fix.

<input type="button" value="Button 1" id="btn1">
<input type="button" value="Button 2" id="btn2">
<input type="button" value="Button 3" id="btn3">

<script type="text/javascript">
	function createEventHandlers(){
		var btn;
		for(var i=1; i <= 3; i++){
			btn = document.getElementById('btn' + i);
			btn.onclick = function(){
				alert('Clicked button #' + i);
			}
		}
	}
	createEventHandlers();
</script>

If you put this code in a page and click the three buttons you will see that all of them will show the message "Clicked button #4". Armed with our understanding of closures we can immediately understand that this bug is being caused by that reference to i used inside the event handler. We can fix that.

function createEventHandlers(){
	var btn;
	for(var i=1; i <= 3; i++){
		btn = document.getElementById('btn' + i);
		btn.onclick = createOneHandler(i);
	}
}

function createOneHandler(number){
	return function() {
		alert('Clicked button #' + number);
	}
}

The above code works because we are not creating functions inside the for loop, hence not producing closures on the same local scope. There is a different set of closures being produced by createOneHandler, but those are not pointing to the same parent scope. Each of these three new closures contain a different scope, created by each call to createOneHandler

Closing thoughts

Closures, of course, are not an exclusive feature of JavaScript. It's a very important trait of functional languages. Even in C#, when we use lambdas, closures are created — many times without us noticing.

The key to properly using closures in our code is to pay attention to locally scoped values from the outer function being used in the body of the inner function. Most of the times this will work as intended by the developer but, when it doesn't, stop and check if more than one closure is sharing the same local scope or if these local values are changing between the inner function creation and its invocation.


Posted 02-23-2009 7:28 AM by sergiopereira

[Advertisement]

Comments

Derick Bailey wrote re: JavaScript, time to grok closures
on 02-23-2009 9:56 AM

you've helped me understand closures much more than I had before. I put together a quick set of tests in c# and am seeing the same behavior for variable scoping that you are showing in javascript. it's good to see that the two languages have the same concept of closures - for 'value' types at least.

thanks, Sergio!

DotNetKicks.com wrote Understand closures with JavaScript
on 02-23-2009 9:59 AM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

sergiopereira wrote re: JavaScript, time to grok closures
on 02-23-2009 10:16 AM

@Derick, glad it helped at least a little. A  good thing in regards to C# is that tools like Resharper will flag some of those situations with an "access to modified closure" warning and even suggest fixes for it.

new ThoughtStream("Derick Bailey"); wrote Closures in C#: Variable Scoping and Value Types vs Reference Types
on 02-23-2009 3:11 PM

I read Sergio’s post on “ Javascript, time to grok closures ” today and it was very enlightening. Overall

Arjan`s World » LINKBLOG for February 23, 2009 wrote Arjan`s World &raquo; LINKBLOG for February 23, 2009
on 02-23-2009 3:22 PM

Pingback from  Arjan`s World    &raquo; LINKBLOG for February 23, 2009

Closures in C#: Variable Scoping and Value Types vs Reference Types | Switch on the Code wrote Closures in C#: Variable Scoping and Value Types vs Reference Types | Switch on the Code
on 02-23-2009 7:10 PM

Pingback from  Closures in C#: Variable Scoping and Value Types vs Reference Types | Switch on the Code

fgantt » Blog Archive » links for 2009-02-23 wrote fgantt &raquo; Blog Archive &raquo; links for 2009-02-23
on 02-23-2009 8:07 PM

Pingback from  fgantt  &raquo; Blog Archive   &raquo; links for 2009-02-23

Reflective Perspective - Chris Alcock » The Morning Brew #293 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #293
on 02-24-2009 3:41 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #293

Timothy wrote re: JavaScript, time to grok closures
on 02-24-2009 9:36 AM

Very informative. Thanks!

Community Blogs wrote JavaScript, inner functions and private members
on 02-24-2009 9:50 AM

This post is part of a series called JavaScript Demystified . In our last installment in this short JavaScript

Christopher Bennage wrote re: JavaScript, time to grok closures
on 02-24-2009 10:32 AM

Your example of a bug resulting from a closure and how to fix it was really good. It communicated the problem very clearly.

Doug Waltman wrote re: JavaScript, time to grok closures
on 02-24-2009 2:39 PM

Thanks for the clear cut explanation, Sergio.  I had seen this behavior before but wasn't able to put it into words like you have.  I definitely understand the mechanics better now.

DotNetShoutout wrote JavaScript, time to grok closures - Sergio Pereira
on 02-24-2009 5:57 PM

Thank you for submitting this cool story - Trackback from DotNetShoutout

Code Monkey Labs wrote Weekly Web Nuggets #53
on 03-02-2009 1:30 PM

Pick of the week: Paying Down Your Technical Debt General Leveraging ILMerge to Simplify Deployment and Your Users’ Experience : Daniel Cazzulino demonstrates how you can easily combine multiple assemblies into a single assembly. DDD Step By Step : Casey

Sergio Pereira wrote JavaScript, inner functions and private members
on 04-01-2009 8:25 AM

This post is part of a series called JavaScript Demystified . In our last installment in this short JavaScript

Travis wrote re: JavaScript, time to grok closures
on 10-02-2009 6:51 PM

Holy Hell... someone asked me about Closures the other day... I didn't know what they were...

Ridiculous how many problems this solves.

Spent about an hour reading some other docs... that gave me a headache and really didn't elucidate anything... then I found your pages.

Awesome, thanks.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)

CodeBetter.Com