As a seasoned JavaScript developer, you might already be aware of nuances of var keyword. JavaScript has been a loose language unless used in strict mode.
- You don’t need to declare a variable before using it, unlike other programming languages that provide better compile time support and syntactic sugar. Variables that are not declared are hoisted to global scope.
- JavaScript Hoists the locally declared variable using var keyword inside a method i.e. no matter where you declare a variable inside your method its always hoisted to top of method and available to all code branches within the method.
- You can declare global functions as well using var keyword by defining them outside a method.
- Since var creates a function scoped variable it can cause unexpected results with loops.
Let’s try to understand various problems occurring because of this and why its concept wise different from a developer coming from C# or Java background.
Scenario 1: Accidentally global variables
Consider below code which accidentally declares a global variable by not using var keyword
<script type="text/javascript"> function globalScope(){ a = 3; //Assigning value without declaring variable //would result in compile time error } globalScope();//Calling function will execute assignment statement and result in variable coming in global scope console.log(a);//Should print 3 here </script>
style="display:block; text-align:center;"
data-ad-format="fluid"
data-ad-layout="in-article"
data-ad-client="ca-pub-5021110436373536"
data-ad-slot="9215486331">
Off-Course you can work-around by running scripts in strict mode using below code to get error instead of accidentally promoting variables to global store if variables are not defined and meant to be local only.
<script type="text/javascript"> "use strict"; function globalScope(){ a = 3; //Assigning value without declaring variable //would result in compile time error } globalScope();//Calling function will execute assignment statement and result in variable coming in global scope console.log(a);//Should print 3 here </script>
style="display:block; text-align:center;"
data-ad-format="fluid"
data-ad-layout="in-article"
data-ad-client="ca-pub-5021110436373536"
data-ad-slot="9215486331">
Scenario 2: Hoisting
Var variables in JavaScript and function scoped i.e. they will be available anywhere in a method where they are declared.
Consider below code which defined a local variable using var keyword and try to understand user experience if they are coming from non-JavaScript background i.e. C# or Java
<script type="text/javascript"> function variableHoisting(i){ if ( i == 1){ var a = 3;//local variable to function. Should not be accessible outside this block i.e. { } } console.log(a);//Should give error since variable is not in scope //in many other programming languages } variableHoisting(1); //Call method to check output variableHoisting(2); //Also Call method to check output //with input that will not set the value of a </script>
Output Comes as
3
Undefined
It looks strange but let’s see how JavaScript looks and modifies this method
Here is modified version from the eyes of compiler
<script type="text/javascript"> function variableHoisting(i){ var a = undefined; if ( i == 1){ var a = 3;// local variable to function but with function level scope. } console.log(a);//Should not give error since variable in scope } variableHoisting(1); //Call method to check output </script>
style="display:block; text-align:center;"
data-ad-format="fluid"
data-ad-layout="in-article"
data-ad-client="ca-pub-5021110436373536"
data-ad-slot="9215486331">
Scenario 3: var as global variable
Consider below code where we have declared variable a outside a method. Once declared it comes in global scope and will be accessible in other script blocks as well.
<script type="text/javascript"> var a =10; function globalVariableSetter(i){ if ( i == 1){ a = 3;//Access to global variable } } globalVariableSetter(1); </script> <script type="text/javascript"> function globalVariableAccessor(i){ console.log(a) } globalVariableAccessor(); </script>
As you are now familiar with nuances of JavaScript, let’s see how new enhancement in ES-6 which is a feature addition to JavaScript as well is going to help us work around the global hell
ES-6 has introduced let keyword which helps in defining scopes in a better way.
Let keyword is block-scoped unlike var keyword which is function scoped
Let’s rewrite code in scenario 2 using let keyword
<script type="text/javascript"> function variableHoisting(i){ if ( i == 1){ let a = 3;//local variable to function. //Should not be accessible outside this block i.e. { } } console.log(a);//Now gives variable not defined exception //which is what we expect } variableHoisting(1); </script>
style="display:block; text-align:center;"
data-ad-format="fluid"
data-ad-layout="in-article"
data-ad-client="ca-pub-5021110436373536"
data-ad-slot="9215486331">
Output:
SCRIPT5009: ‘a’ is undefined
Scenario 4: var inside loop statements
In below code, we are pushing a function inside a function array. As per logic, the function will log the captured value of index once invoked and expected out is numbers from 0 to 4. Let’s check
<script type="text/javascript"> var functionArray = []; for (var index = 0; index < 5; index++) { functionArray.push(function(){console.log(index)}); } //Try to print all values functionArray.forEach(function(item) { item(); }, this); </script>
Output:
5
5
5
5
5
So, the output is not what we expected. Since variable index goes into function scope and when we actually invoke functions, they used the current value of index i.e. 5. While creating functions that do not return immediately (like in our example) this is a real cause of concern and we can work-around by create IIFE (Immediately Invoked function expressions) which actually capture the value of variables rather than delaying it till actual usage. Here is an IIFE version above code
<script type="text/javascript"> var functionArray = []; for (var index = 0; index < 5; index++) { functionArray.push(function (value) { return function () { console.log(value); } }(index)); } //Try to print all values functionArray.forEach(function(item) { item(); }, this); </script>
style="display:block; text-align:center;"
data-ad-format="fluid"
data-ad-layout="in-article"
data-ad-client="ca-pub-5021110436373536"
data-ad-slot="9215486331">
Above code works as expected and prints value 0-4. This is because the current value of variable at the time of loop execution was captured inside the arrays. But as you can see we need to work around something which is available by default in other programming language. And again let keyword comes to rescue.
Below is a refactored version using let keyword which works as expected without workaround
<script type="text/javascript"> var functionArray = []; for (let index = 0; index < 5; index++) { functionArray.push(function () { console.log(index) }); } //Try to print all values functionArray.forEach(function(item) { item(); }, this); </script>
Output:
0
1
2
3
4