Var, Let, Const in JavaScript + scope and hoisting
Tags: javascript, tutorial, webdev, beginners
Published: 2021-03-12 15:07:21

Originally posted on [bigsondev.com](https://bigsondev.com/blog/var-let-const-best-practices-and-differences/)

---

*Everyone needs variables in JavaScript. Let's learn the best ways to use them and how to avoid pitfalls that might cause unwanted side effects or introduce bugs in the code.*

---

## Introduction
Before the advent of ES6, there were only two ways to declare variables - global variables or using var keyword (function or global scope).

With ES6, let and const keywords were introduced. Both can either have a global or block scope. let can be updated, but not redeclared. const as the name implies, can't either be updated or redeclared.

---

## Variables
Let's learn the details about each of the above keywords by looking at the code examples. After that, we'll sum up recommendations and best practices.

---

### 👿 Global
Globals are evil.

```javascript
console.log(window.setTimeout); // function setTimeout() { [native code] } 
​
setTimeout = 'Hello World';
​
console.log(setTimeout); // POLLUTED GLOBAL SCOPE: "Hello World"
console.log(window.setTimeout); // OVERRIDDEN WINDOW VARIABLE: "Hello World"
```

In the above example, we declared the setTimeout variable without using any keyword and assigned the Hello World string to it. This way, the variable became **global**. Additionally, we accidentally overwrote the existing setTimeout function and it'll cause unwanted behavior.

The [window](https://www.w3schools.com/jsref/obj_window.asp) object has a lot of built-in properties that shouldn't be overridden. Unless you have a good reason (e.g. [polyfill](https://developer.mozilla.org/en-US/docs/Glossary/Polyfill), custom implementation), you should strive away from overriding existing variables, functions in the window object.

In the later sections, I'll explain how to avoid global variables overrides, for now, try to study the window object and make sure you don't come up with the exact same name when naming your variables.

---

### ❌ Var
var has the same problems as global variables, but it can be used in a function scope to not pollute the global one.

```javascript
// message variable has a function scope, 
// it'll only be available in the hello function
var hello = () => {
  var message = 'Hello World';
​
  return message; 
};
​
// message variable has a global scope,
// it'll be included in the window object
var message = 'Hello People';
​
console.log(message); // POLLUTED GLOBAL SCOPE: "Hello People"
console.log(window.message); // ADDED TO WINDOW: "Hello People"
console.log(hello()); // "Hello World";
```

It's good that we didn't overwrite the outer message variable inside of the hello function as the inner message variable had a function scope which is helpful in avoiding leaking of the variables to outer scopes. However, the outer message variable still polluted the global scope.

Another bad thing about using var is that it can be redeclared and updated which breaks the immutability approach of functional, declarative programming:

```javascript
var canBeChangedAndUpdated = 42;
var canBeChangedAndUpdated = 43;
​
if (true) {
  var canBeChangedAndUpdated = 44;
}
​
console.log(canBeChangedAndUpdated); // 44
```

We redeclared and updated canBeChangedAndUpdated variable 3 times and it was applied to the global scope.

In JavaScript, there is a concept of [Hoisting](https://developer.mozilla.org/en-US/docs/Glossary/Hoisting) which var also can't handle properly. 🙈 It means that variable declarations are moved all the way to the top of the global scope or function scope. Let's see how that works.

```javascript
console.log(x); // undefined
console.log(y); // ReferenceError: y is not defined
​
var x = 5;
```

**Only the declaration of x was hoisted as it printed undefined**. The best practice is to always include variable declarations and assigning values to them (in most scenarios) at the top as using hoisting is confusing and difficult to reason about. It should look like below.

```javascript
var x = 5;
​
console.log(x); // 5
console.log(y); // ReferenceError: y is not defined
```

We got 5 when printing it through console.log which is good. y variable throws an error as it never was defined.

**var keyword gives too much "flexibility" and doesn't have strict rules. I don't use it anymore and couldn't be happier.**

Let's see what improvements ES6 keywords bring to us.

---

## ⚠️ Let
The first improvement is that let doesn't add to the window object when declared as a global variable. However, it's still polluting the global scope if used like below.

```javascript
console.log(window.setTimeout); // function setTimeout() { [native code] } 
​
let setTimeout = 'Hello World';
​
console.log(setTimeout); // POLLUTED GLOBAL SCOPE: "Hello World"
console.log(window.setTimeout); // ALL GUCCI, WINDOW OBJECT UNTOUCHED: function setTimeout() { [native code] }
```

The second improvement is a block scope of let. Let's see it in action.

```javascript
// global firstName variable
let firstName = 'John';
​
// another firstName variable is declared inside
// of the if block, it won't change the 
// global firstName
if (true) {
  let firstName = 'Jane';
​
  console.log(firstName); // "Jane"
}
​
// firstName is still "John"
console.log(firstName); // "John"
```

Outer name variable polluted global scope but inner (inside if statement) lived only there. Block scope is helpful in avoiding leaking of variables to outer scopes similar to function scope.

The third improvement is that let can't be redeclared, let's see what happens if we try to do that.

```javascript
let canOnlyBeUpdated = 42;
let canOnlyBeUpdated = 43; // Uncaught SyntaxError: Identifier 'canOnlyBeUpdated' has already been declared
```

We get an error that notifies us canOnlyBeUpdated variable can't be redeclared.

It can still be updated which contradicts the immutability concept.

```javascript
let canOnlyBeUpdated = 42;
canOnlyBeUpdated = 43;
​
console.log(canOnlyBeUpdated); // VARIABLE UPDATED: 43
```

And if it comes to hoisting, let deals with it in a bit more strict way than var.

```javascript
console.log(x); // ReferenceError: Cannot access 'x'
​
let x = 1;
```

Hoisting still occurs but let lands in [Temporal Dead Zone](https://www.freecodecamp.org/news/what-is-the-temporal-dead-zone/) thus it's not accessible and we get an error. x should be declared and assigned (in most cases) before it's used.

Let's jump to the perfect one (almost) which is a const keyword.

---

### ✅ Const

The great thing about const is that it has all the good properties from let - block-scoped, can't be redeclared and additionally, it can't be updated. 😍

```javascript
const canOnlyBeUpdated = 42;
​
canOnlyBeUpdated = 43; // Uncaught TypeError: Assignment to constant variable."
```

const keyword perfectly fits in functional, declarative programming with immutability in mind.

But the **almost** mentioned before.

```javascript
const person = {
 age: 28,
 name: 'Adrian'
}
​
person.name = 'Banana';
​
console.log(person); // { age: 28, name: "Banana" }
```

Whoops, we updated the property of the person object, not so immutable. There are caveats you'll face in JavaScript, take a look at [this article](https://medium.com/better-programming/javascript-prevent-objects-from-being-changed-d1ca82f02975) about preventing modifications to an object.

An additional example of how good it feels to write code using const can be found below.

```javascript
const multiply = (a, b) => a * b;
​
const price = 100;
const numberOfPeople = 5;
​
const amount = multiply(price, numberOfPeople);
​
console.log(amount); // 500
```

---

## Avoiding global scope
The simplest solution is to use a function or block scope. If you need something more organized, create a namespace to avoid name collisions.

```javascript
window.MY_NAMESPACE = {};
window.MY_NAMESPACE.setTimeout = 'Hello World';
​
console.log(window.setTimeout); // function setTimeout() { [native code] }
console.log(window.MY_NAMESPACE.setTimeout) // "Hello World"
```

This way, the built-in window.setTimeout is untouched and we can declare our variables in the namespace.

There are more solutions for avoiding global variables in JavaScript. Similarly like in CSS there are CSS-in-JS solutions, BEM, SMACSS. Study the below list to avoid creating global variables.

- [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE)
- [Module and Revealing Module patterns](https://css-tricks.com/implementing-private-variables-in-javascript/#the-module-design-pattern)
- [ES6 Classes](https://css-tricks.com/implementing-private-variables-in-javascript/#enter-es6-classes)
- [Webpack variables hoisting](https://medium.com/webpack/brief-introduction-to-scope-hoisting-in-webpack-8435084c171f)

Let's dive into the summary of recommendations & best practices.

---

## Summary
You probably guessed my preference about const but honestly, as Dan Abramov said in [his post](https://overreacted.io/on-let-vs-const/), "I don't care". It's all about conventions agreed with the entire team. Make sure it fits all of you and set linters appropriately.

Below you can find my recommendations:
- Aim for const in most cases and block scope, minimize let to the bare minimum, don't use var.
- Strive away from global scope pollution, use e.g. webpack in your projects.
- Stick to semantic, functional programming with immutability in mind, free of side effects, not redeclaring, and updating existing variables.

---

## Closing Notes
I hope it was something and you could've learned a ton from the article. We went through an explanation of hoisting, scopes, and variable keywords. Additionally, you've acknowledged best practices and proposed solutions to avoid global scope pollution.

---
I'm thinking about creating YT video series - building [projects](https://bigsondev.com/projects/) from my website, step-by-step, modern technologies, best coding practices with a thorough explanation.

If you enjoy the content and like the idea:
[<img src="https://img.buymeacoffee.com/button-api/?text=Buy me a pizza&emoji=🍕&slug=bigsondev&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff">](https://www.buymeacoffee.com/bigsondev)

Thanks for all the support! ❤️

Get in touch: [Mentorship](https://bigsondev.com/mentorship/) | [Twitter](https://twitter.com/BigsonDev) | [LinkedIn](https://www.linkedin.com/in/adrian-bigaj-282277130/)