Jan 24, 2022

JS Variables 101: Var, Let and Const?

Yet another blog explaining Var, Let and Const but I promise this is the only one you need.

ECMASCRIPT2015, also known as ES6 introduced a lot of awesome features. One of the features was the addition of `let` and `const` for declaring variables. Previously developers used `var` for variable declaration, so what was the need of bringing new ones?

If you don't know what `let` and `const` brings to the table and how are they different from each other, this blog post is for you. I will compare them based on usage, scope, and hoisting.

Var

Before the introduction of ES6, the only way to define variables in JavaScript was to use the `var` keyword. For many years, it worked fine, however `var` differs from other programming languages in terms of variable scoping leading to unwanted bugs that are hard to track.

The below code example demonstrates the declaration of a variable with the `var` keyword:

1var declaredWithVar = "welcome to the blog";
2console.log(declaredWithVar);  //welcome to the blog
Scope

The Scope refers to the present context of code, which decides the accessibility of the variables.

The scope is of two types Global and Local:

- Variables that are declared outside of a block are known as Global variables.

- Variables that are declared inside of a block are known as Local variables.

The variables declared with `var` are globally scoped when declared outside a function. Any variable declared with `var` outside of a function block is accessible across the whole window.

`var` is a function scoped when it is declared within a function which means that it is accessible within that function only.

Look at the example below to understand further:

1var globalScopedVar = "declared globally"
2function varScopeCheck(){
3  var scopingOfVarInFunc = "declared inside function"
4  console.log(scopingOfVarInFunc)
5}
6console.log(scopingOfVarInFunc) //Uncaught ReferenceError: scopingOfVarInFunc is not defined
7console.log(varScopeCheck()) //declared inside function
8console.log(globalScopedVar) //declared globally

As you can see, we cannot access `scopingOfVarInFunc` outside of the function as the variable is locally scoped but we can access the `globalScopedVar` as it is globally scoped.

`var` can also be redeclared and updated.

This means the value of the variable can be updated by reinitializing and the variable declared with the `var` keyword can be declared again and again with the same or different values.

Look at the example below to understand further:

1var declaredVar = "First time"
2var updatedVar = "Old value"
3var declaredVar = "Second time"
4updatedVar = "New value"
5console.log(declaredVar) // Second Time
6console.log(updatedVar) // New value
Hoisting

Hoisting is the process by which the interpreter allocates memory for variable and function declarations prior to executing the code. This allows us to use a variable before it has been declared and initialized.

For example:

1console.log(hoistedVar); //undefined
2 var hoistedVar = "I'll be hoisted"

why `undefined`? why `not defined` error?

`var` variables are hoisted to the top of the scope and initialized with the value `undefined`.

The Problem with var
1var nameUsingVar = "Michael"
2if(true){
3  var nameUsingVar = 'Mike instead'
4}
5console.log(nameUsingVar) // Mike instead

In the above code example, global scoped `nameUsingVar` is replaced by the block-scoped `nameUsingVar` and we get the unexcepted value. Well, it is not a problem if it's intentional but imagine managing your variables after 1000s of lines of code. This will become tricky to work with and cause a lot of bugs in your code.

That is why `let` and `const` were introduced and widely used.

Let

`let` came as an improvement over `var` by being `block-scoped` which solves the problem discussed above.

The below code example demonstrates the declaration of a variable with the `let` keyword:

1let declaredWithLet = "I am preferred over var";
2console.log(declaredWithLet);  //I am preferred over var
Scope

Variables declared with `let` are block-scoped which means that a variable declared in a block with `let` is only available for use within that block. Variables declared outside blocks are global scoped.

Let's understand it with an example:

1let globalScopedLet = "declared globally"
2function letScopeCheck(){
3  let scopingOfLetInFunc = "declared inside function"
4  console.log(scopingOfLetInFunc)
5}
6console.log(scopingOfLetInFunc) //Uncaught ReferenceError: scopingOfLetInFunc is not defined
7console.log(letScopeCheck()) //declared inside function
8console.log(globalScopedLet) //declared globally

It solves the problem with `var`:

1let nameUsingLet = 'Michael'
2if(true){
3	let nameUsingLet = 'Mike'
4}
5console.log(nameUsingLet) //Michael

As you can see, we get the expected output as it is block scoped.

`let` cannot be re-declared but can be updated within a scope block.

1let nameUsingLet = 'Michael'
2let nameUsingLet = 'Mike'
3//SyntaxError: Identifier 'greeting' has already been declared
4if(true){
5  /* This is a different scope, so redeclaration here is ok.*/
6	let nameUsingLet = 'Michel'
7    console.log(nameUsingLet) //Michel
8}
9console.log(nameUsingLet) //Michael
Hoisting

`let` declarations are hoisted but it's different from `var`.

1console.log(variableUsingLet); // ReferenceError: Cannot access 'a' before initialization
2console.log(variableUsingVar); // prints undefined as expected
3let variableUsingLet = 10;
4console.log(variableUsingLet); // 10
5var variableUsingVar = 15;
6console.log(window.variableUsingLet); // undefined
7console.log(window.variableUsingVar); // 15

It looks like `let` isn't hoisted, but it is, let's understand:

Both `variableUsingLet` and `variableUsingVar` are actually initialized as undefined in hoisting stage. But `variableUsingVar` is inside the storage space of GLOBAL, and `variableUsingLet` is in a separate memory object called script, where it can be accessed only after assigning some value to it first ie. one can access `variableUsingLet` only if it is assigned. Thus, it throws a `ReferenceError`.

Temporal Dead Zone: Time elapsed since the `let` variable was hoisted until it was initialized with a value.

So any line till before "let variableUsingLet = 10" is the Temporal Dead Zone for `variableUsingLet`.

Since `variableUsingLet` is not accessible on global, it's not accessible in window/this also.

`ReferenceError` is thrown when variables are in the Temporal Dead Zone, `SyntaxError` doesn't even let us run a single line of code.

Finally, let’s learn about const.

Const

Just like the name, `const` variables are constant, they cannot be redeclared or updated and if we try to do so, we will get an error.

With only `var`, the way to signify a constant variable was to write the variable name in all caps but it still could be updated and redeclared. `const` solves this problem along with `var` scoping problem.

1const variableUsingConst = 10;
2variableUsingConst = 20;
3console.log(variableUsingConst)
4//TypeError: Assignment to constant variable.
Scope

It is also block-scoped and works similarly to `let`.

1const nameUsingConst = 'Michael'
2if(true){
3	const nameUsingConst = 'Mike'
4}
5console.log(nameUsingConst) //Michael
Hoisting

It is similarly hoisted as `let`.

1console.log(variableUsingConst); // ReferenceError: Cannot access 'variableUsingConst' before initialization
2const variableUsingConst = 10;
3console.log(variableUsingConst); // 10

Conclusion

-> Try using `const` wherever possible.

-> If not use `let`, Avoid `var`.

-> Declare and initialize all variables with `let` to the top to avoid errors and shrink the Temporal Dead Zone window to zero.

Harsh

Harsh

Loves to Code 👨‍💻 | React Junkie ⚛️ | Learning and sharing 📚

Leave a Reply

Related Posts

No posts right now.

Categories