DEV Community

Cover image for Why don’t we use var anymore?
Coding Sam
Coding Sam

Posted on • Originally published at Medium on

Why don’t we use var anymore?

If you used to code in Javascript in the old days, you used the var keyword a lot. There was no other way to declare a variable. It was really simple. The only thing you needed to do was just something like this:

var x = 3; var y = 'hello'; // etc...

Since ES6, there are two new keywords, const and let. The first one is a bit self-explanatory, it allows you to define a constant. If you do:

const x = 2;

You can’t reassign another value to the constant x. There are many programming languages that allow developers to define constants. This is a good way to write less error-prone code. However, there is also this “let” keyword, which allows you to declare variables. And that’s all you need, just another keyword that does the same as var, right? Why there is not just “var” and const?

Why don’t we use var anymore?

To answer that question, you need to know how var really works. In Javascript, before ES6, there was no block scope. You have function scope instead. Let’s break down these two concepts.

Block scope

When you declare a variable or a function, it will be accessible inside the block it was declared on. Most programming languages have this type of scope, for instance, Java. See the code snippet below:

public class Example () {
  public void processArray (String[] array) {
    for(int i = 0; i < array.length; i++) {
      System.out.println(array[i]);
    }

    System.out.println("I cannot use the variable i here");
  }
}

You can only use the variable i inside the for loop. It doesn’t exist outside that block. In Java, each time you use a “{“ you are creating a new block and the “}” means, you are closing that block. Of course, if you declare a variable outside the block, you can use it inside the block.

Let me show you the following diagram that illustrates how the block scope would work in this example:

Block scope using the Example class.

Each rectangle is a scope. The “children” scopes can access the functions and variables in the “parent” scopes, but the “parents” cannot access the “children”. In this example, the Example class is a parent of processArray method, which is a parent of the for loop block. The Example class cannot access whatever belongs to the processArray method, which cannot access whatever belongs to the for loop. However, the for loop can access anything in the processArray method and anything in the Example class, for instance, a variable or any other method. This is the kind of scope that most developers are used to work with.

Function scope

Unlike Java, Javascript (ES5) creates scopes based on functions. This means, once you declare a variable inside a function, you can use it anywhere in that function.

function processArray (array) {
  for(var i = 0; i < array.length; i++) {
    console.log('Element ', array[i]);
  }

  console.log('I can use variable i outside the loop ', i);
}

Of course, you also have the global scope. Each time you declare a variable outside a function, it will belong to the global scope.

Let me show you another diagram, but this time, for the function scope:

Function scope for the processArray function

Looks much simpler, right? But where is the for loop?

The for loop is a block, but there is no block scope here, that’s why, it does not have its own scope.

Why do we stop using the var keyword?

It’s all about scope! Most programming languages have block scope. Why? Because it is less error-prone. You can declare variables inside blocks (if statements, for loops and so on) without worrying about overwriting some previously declared variable.

Let me show you an example. Let’s say you are implementing a function that prints each position of a matrix. You will write a for loop inside another for loop. In ES5, where you only have the var keyword available, a beginner would write something like this:

function printMatrix (matrix) {
  for (var i = 0; i < matrix.length; i++) {
    var line = matrix[i];
    for (var i = 0; i < line.length; i++) {
      var element = line[i];
      console.log(element);
    }
  }
}

var matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

printMatrix(matrix);

The output will be:

Output of printMatrix function in ES5

It only logged the first line. To help you understand what is going on, let me show you a scope diagram for this example:

Breaking down the scope in the printMatrix ES5 version

All variables inside the printMatrix function are on the same level. The two for loops are actually using the same variable i!

What happened? When I started coding in Javascript some years ago, I wrote a lot of code like this, because I used to code a lot in Java, which has block scope. So, I thought that if I declared a variable inside the loop, it will stay there… But not really.

After a lot of bugs and frustration I learned that Javascript does not have block scope. There is only function scope. But, even after I learned about it, I forgot a lot of times! This is something that is really easy to forget about. The usual expectation is that, in the second for loop, you are creating a new variable. But you are not. You are just overwriting the variable i in the first for loop. After running the second loop, the condition of the first one will be evaluated again, but the variable i now has the value 3 (the size of the first line in the matrix), which is equal to the matrix length (3). The condition returns false and the loop stops. That’s why only the first line of the matrix gets logged.

In Javascript, it doesn’t matter how many times you use the keyword “var”. If it’s the same name in the same function, you are pointing to the same variable.

This function scope can be a source of a lot of bugs. Fortunately, Javascript has been changing and now we have ES6 and more. There are these two “new” keywords, const and let, which allow you to define a constant and a variable, respectively. They both work with block scope, which means, if variables or constants are declared inside a block, they will not be available to the “parent” blocks.

Let’s rewrite the previous example, logging the matrix, taking advantage of these new keywords. If you replace var by let and add use const for things you know you will need to need to change after initialization…

function printMatrix (matrix) {
  for (let i = 0; i < matrix.length; i++) {
    const line = matrix[i];
    for (let i = 0; i < line.length; i++) {
      const element = line[i];
      console.log(element);
    }
  }
}

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
];

printMatrix(matrix);

The output will be:

Output of printMatrix function in ES5

All positions in the matrix got logged. It just worked and I just had to replace var by let and const keywords! Because in each for loop I am actually creating a different variable i. In the second loop the first i will not get overwritten.

Let’s see what happened under the hood in terms of scope:

Breaking down the scope in the printMatrix ES6 version

Seems a bit more complicated, but this gives you block scope and each for loop has its own variable i. In the old days Javascript, you would need to give different names to avoid this naming conflict. But seriously, when you need to do a for loop to iterate over an array, the first name that comes to your head, for the current array index value is “i”, isn’t it?

Just a small note : I know that you have forEach, map, and more functions to deal with arrays. This is just a simple example to show how the var, let and const keywords work.

Conclusion

let and const are not just two new cool keywords, they also introduce block scope that allows us to write clean and less error-prone code.

Why don’t we use var anymore? Because now there is a better way of declaring variables and even constants… With block scope! You don’t need to think twice when declaring variables inside blocks. I think that is easier to work with block scope than with function scope. The var usage has being been discouraged. For instance, if you use ESLint to check your code, you can configure a “no-var” rule that throws an error if there is any var being used.

Please, keep in mind that you should use a transpiler tool, like Babel, to transpile your code from ES6 to ES5, to make sure it will run in any browser. Not all browsers support the complete ES6 specification yet.

Embrace let and const and let var go forever!

I hope you enjoyed and learned something. Happy coding! :)

Let me know what you think and follow me for more cool content about dev stuff :)

Discussion (7)

Collapse
johncip profile image
jmc

My personal advice is to embrace const but keep let at arm's length :P

Collapse
codingsam profile image
Coding Sam Author

Actually, I don't use let that often. Most times I just use const. :P

Collapse
tiffany profile image
Tiffany White

I actually use let in the body of a for loop as it checks which names to bind then copies all the values to those names on each iteration.

Collapse
sql_knievel profile image
Rich Boniface

Hey Coding Sam - nice article - just wanted to point out a typo that might confuse some readers:

  "The “children” scopes can access the functions and variables in the “parent” scopes, but the “children” cannot access the “parents”. 

I think you meant to say at the end, "but the parents cannot access the children."

Collapse
codingsam profile image
Coding Sam Author

Yes you are completely right. Sorry. Just fixed it :)

Collapse
igorsushko1 profile image
IgorSushko1

thank you for simple language! it was easy to read!

Collapse
mohamedhamada profile image
Mohamed Hamada

Easy to read and understand! thanks bro