Although var
used to be the provided way to declare variables in JavaScript. There are many pitfalls with using the var
syntax for declaring your variables, objects or arrays. There are some serious issues with using the var
keyword, and should not be used in your application code!
In the bad old days, the only way you could declare a variable in JavaScript was to use the var
keyword:
var i = 10;
var name = 'pedbad';
var obj = {};
var arr = [];
The major issue with using var
is that it was not block scoped. In JavaSCript before the introduction of ES6 only function scope and global scope were available.
So what is scope? Scope is the context in which variables and functions are accessible. Scope determines the accessibility (visibility) of variables. JavaScript has three main types of Scope:
Global scope
A variable with Global scope is visible everywhere. All scripts and functions can access it. A variable declared outside a function becomes global.
Function scope
Variables defined inside a function are not accessible (visible) from outside the function.
Block scope
Variables declared inside a
{ }
block cannot be accessed from outside the block
One problems with declaring variables with the var
keyword is that you can easily overwrite variable declarations:
var studentName = 'John';
//sometime later in your code,
//you can accidentally overwrite your variable
//by re-declaring it
var studentName = 'Jane';
console.log(studentName);
//output : Jane
In the code above, the studentName variable is initially declared as John, but is then accidentally redeclared to be Jane. The console then displays the string Jane.
In Javascript, it doesn’t matter how many times you use the keyword
var
. If it has the same name in the same function, you are pointing to the same variable.
In non trivial applications as your codebase becomes very large, you might accidentally overwrite a variable that you did not intend to change. Because this behaviour does not throw an error, searching for and fixing bugs becomes more difficult.
When you declare a variable with the var
keyword, it is declared globally, or locally if declared inside a function.
function demo() {
var name = "pedbad";
console.log(`inside demo function our name variable is: ${name}`);
}
demo();
//output
//inside demo function our name variable is: pedbad
console.log(`outside demo function our name variable is: ${name}`)
//output
//ReferenceError: name is not defined
As expected the name variable declared inside the demo() function is only accessible (visible) from within the function. However as stated earlier, variables defined inside a function are not accessible from outside the function.
Now lets look at a { }
block scope example:
{
var foo = 'bar';
console.log(`inside our block foo is: ${foo}`);
}
console.log(`outside our block foo is: ${foo}`);
//output
//inside our block foo is: bar
//outside our block foo is: bar
Although we declared our foo variable inside the curly braces, we could still access the variable outside the block. This is a serious problem when using the var
keyword. You would expect the foo variable to be private inside the block, but as stated earlier before the introduction of ES6 JavaScript did not support block scope.
Prior to ES6, the only way we could scope the variable foo to the block only was to use an Immediately Invoked Function Expression or (IIFE).
Using this design pattern we could scope our variable inside the block:
(function () {
var foo = 'bar';
console.log(`inside our block foo is: ${foo}`);
})();
//output:
//inside our block foo is: bar
console.log(`outside our block foo is: ${foo}`);
//output
//ReferenceError: foo is not defined
Lets look at another issue with using var
:
var numArray = [];
for (var i = 0; i < 5; i++) {
numArray.push(i);
}
console.log(numArray);
//output:
//[ 0, 1, 2, 3, 4 ]
console.log(i);
//output:
//5
Our iterator variable i is still accessible outside the for loop! With the var keyword, i is declared globally. So when i++ is executed, it updates the global variable. This code is similar to the following:
var numArray = [];
var i;
for (i = 0; i < 5; i++) {
numArray.push(i);
}
console.log(numArray);
//output:
//[ 0, 1, 2, 3, 4 ]
console.log(i);
//output:
//5
This behaviour will cause problems if you were to create a function and store it for later use inside a for loop that uses the i variable. This is because the stored function will always refer to the value of the updated global i variable.
var printNumZero;
for (var i = 0; i < 5; i++) {
if (i === 0) {
printNumZero = function() {
return i;
};
}
}
console.log(printNumZero());
//output:
//5
As you can see, printNumZero() prints 5 and not 0. This is because the value assigned to i was updated and the printNumZero() returns the global i and not the value i had when the function was created in the for loop.
Lets explore another example:
for (var i = 0; i < 5; i++){
setTimeout(function(){
console.log(`inside for loop i is: ${i}`);
}, i*1000);
}
//output:
//inside for loop i is: 5
//inside for loop i is: 5
//inside for loop i is: 5
//inside for loop i is: 5
//and so on...
Again, as our variable i is not block scoped, we only see 5 as our output. This is because by the time the setTimeOut function is called, our variable i has already been assigned the value of 5.
These are all serious issues with using var
and that is one of the reasons in ES6 let and const were introduced to enable block scoping in JavaScript.
Lets take a look at our setTimeout example , this tile using the let
syntax instead of var
for (let i = 0; i < 5; i++){
setTimeout(function(){
console.log(`inside for loop i is: ${i}`);
}, i*1000);
}
//output:
//inside for loop i is: 0
//inside for loop i is: 1
//inside for loop i is: 2
//inside for loop i is: 3
//inside for loop i is: 4
console.log(`outside for loop i is: ${i}`);
//output:
//ReferenceError: i is not defined
Due to correctly block scoping our variable, we get the expected behaviour. Another benefit is that we cannot accidentally overwrite a variable that is declared using the let or const syntax. Remember our original example with the studentName variable, if we use the modern ES6 syntax, we get an error:
//Redeclaration. Not Good with Let
let studentName = 'John';
//sometime later in your code,
//you can accidentally overwrite your variable
//by re-declaring it
let studentName = 'Jane';
console.log(studentName);
//output
//SyntaxError: Identifier 'studentName' has already been declared
The keyword let
is not the only new way to declare variables. In ES6, you can also declare variables using the const
keyword. Using the const
keyword we can declare Read-Only Variables in JavaScript. Once a variable is assigned with const, it cannot be reassigned. When using const
it is common for developers to use uppercase variable identifiers:
//JavaScript const variables must be assigned a value when they are declared:
const PI = 3.1415
PI = 2;
//TypeError: Assignment to constant variable.
Variables defined with
const
cannot be Redeclared or Reassigned. Useconst
when you know that the value should not be changed.
One important point to highlight is that the const
declaration protects only the reference to the assigned object; it does not protect the object itself, for example:
You can create a constant object {}
or array []
:
const FRUITS = ["Apple", "Banana"];
const STUDENT = {name:"John", age:"20", id:"1234"};
// You can change an element:
FRUITS[0] = "Orange";
// You can add an element:
FRUITS.push("Melon");
// You can change an object property:
STUDENT.name = "Jane";
// You can add a property:
STUDENT.lastName = "Doe";
But you CAN NOT reassign the object or array:
FRUITS = ["Audi", "BMW", "Chrysler"]; //Error
STUDENT = {car:"Audi", color:"red"}; // ERROR
If you need to protect the whole object, then you can use the Object.freeze() method but be warned this is only a shallow freeze and will not work for nested objects or arrays.
globalThis
In ES2020 a new keyword was added, the globalThis
and as the name implies it is literally a Global this
Although we already have the window
keyword to access the Global objects, historically, accessing the global object has required different syntax in different JavaScript environments.
On the web you can use window, self, or frames - but in Web Workers only self
will work. Also in Node.js none of these work, and you must instead use global
.
This difference is syntax to access global environment variables was confusing and to solve it the globalThis
syntax was introduced to standardise accessing the global this
value.
In this way, you can access the Global Object in a consistent manner without having to know which environment the code is being executed.
References