2009-11-12
The perils of the missing javascript block scope
Javascript variable scope is something of a strange thing. You have a global and a function scope. But unlike most other languages, there is no block scope! This is often a cause for hard to find bugs.Let me show you the consequences of this with a small puzzler:
<html>
<body>
<a id="link1" href="">Link 1</a> <span id="part1"></span><br/>
<a id="link2" href="">Link 2</a> <span id="part2"></span><br/>
<a id="link3" href="">Link 3</a> <span id="part3"></span><br/>
<a id="link4" href="">Link 4</a> <span id="part4"></span><br/>
<a id="link5" href="">Link 5</a> <span id="part5"></span><br/>
No Link 6 <span id="part6"></span><br/>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
for (i = 1; i <= 5; i++) {
$("#link"+i).click(function() {
$("#part"+i).html("Clicked !");
return false;
});
}
});
</script>
</body>
</html>
Now what does this do? (you need a bit of jquery knowledge to guess it)
At first glance, it walks over the 5 links present on this page, and registers a handler for mouse clicks that adds the text "Clicked !" to the span element with the same number as the link.
Or does it?
Well, this is what the output looks like when you click on "Link 3":
So how can we fix this?
We have to separate the scopes, and to do that we need a new function. This is what the correct code looks like:
<html>
<body>
<a id="link1" href="">Link 1</a> <span id="part1"></span><br/>
<a id="link2" href="">Link 2</a> <span id="part2"></span><br/>
<a id="link3" href="">Link 3</a> <span id="part3"></span><br/>
<a id="link4" href="">Link 4</a> <span id="part4"></span><br/>
<a id="link5" href="">Link 5</a> <span id="part5"></span><br/>
No Link 6 <span id="part6"></span><br/>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
for (i = 1; i <= 5; i++) {
addClickEvent(i);
}
});
function addClickEvent(i) {
$("#link"+i).click(function() {
$("#part"+i).html("Clicked !");
return false;
});
}
</script>
</body>
</html>
With the registration of the click event taking place in a separate function (and therefore a separate scope for the variable i), this is what the output looks like:
So beware of the lack of block scope in Javascript! It can come and bite more easily than you think if you're not extra careful about it!
About Axel Fontaine
I'm an entrepreneur, public speaker and software development expert based in Munich.
I'm the creator of Sprinters. Sprinters lets you run your GitHub Actions jobs 10x cheaper on your own AWS account with secure, ephemeral, high-performance, low-cost runners within the privacy of your own VPC.
I also created CloudCaptain, previously known as Boxfuse. CloudCaptain is a cloud deployment platform enabling small and medium size companies to focus on development, while it takes care of infrastructure and operations.
Back in 2010, I bootstrapped Flyway, and grew it into the world's most popular database migration tool. Starting late 2017, I expanded the project beyond its open-source roots into a highly profitable business, acquiring many of the world's largest companies and public institutions as customers. After two years of exponential growth, I sold the company to Redgate in 2019.
In the past I also spoke regularly at many large international conferences including JavaOne, Devoxx, Jfokus, JavaZone, JAX and more about a wide range of topics including modular monoliths, immutable infrastructure and continuous delivery. As part of this I received the JavaOne RockStar speaker award. As a recognition for my contributions to overall Java industry, Oracle awarded me the Java Champion title.
You can find me on 𝕏 as @axelfontaine and email me at axel@axelfontaine.com