When to use var, let, and const in JavaScript

When to use var, let, and const in JavaScript

  • 6/22/2019
  • 339 views

When I first started learning Javascript I had a hard time wrapping my head around when to use the varlet, and const keywords.

I was used to writing native iOS apps in Swift (and Objective-C when push came to shove). In Swift, the appropriately named var is used to create re-assignable variables. The not-so-appropriately named let is used to create constants. And then there’s the static and class keywords used to create typed properties.

As you’ll read below, this has almost nothing to do with the way we do things in JavaScript ES6, which was initially confusing to me. My goal for this post is to demystify the use of varlet, and const to help you understand when you should use which.

Before we dive in, it’s important that you understand the following terms:

  • Scope
  • Hoisting
  • Assignment

Don’t worry if they’re unfamiliar, I’ll explain what they are and how they tie in along the way.

One of the biggest differences between varlet, and const comes down to their scope.

In computer programming, the scope is a term used to tell us how accessible an object (or variable) is. Think about it as a perimeter drawn around that variable where only the code that is within that perimeter can access that variable. The “narrower” the scope, the more restrictive it is to access.

In ES6, a var can have two types of scope: a global scope, and a function scope.

var with global scope is accessible throughout your entire JavaScript code. If we continue with our above analogy, we could say that its perimeter is the window object in which your entire code is running.

var with a function, the scope is only accessible within its enclosing function. We could say that its perimeter is limited to the function that declares it.

// Variable a has a global scope
var a = 'hello';

function doSomething() {
  // Variable b has a function scope
  var b = "what's up";
  
  console.log(a); // The console prints: hello
  console.log(b); // The console prints: what's up
}

console.log(a); // The console prints: hello
console.log(b); // ❌error: Uncaught ReferenceError: b is not defined

If we were to run this code, the compiler would have no problem console logging a inside or outside the function since a is declared at the top of the file with a global scope. b is declared inside our doSomething() function so it’s accessible within the function but the compiler throws an error when we try to access b outside the function since it’s not globally scoped.

Seems pretty straight forward and looks a lot like Swift (minus the incessant semi-colons everywhere 🥺).

But we’re talking about JavaScript here people, it doesn’t end there. Take a look at what happens when we add a for loop into the mix…

var a = 'hello';

function doSomething() {
  
  var b = "what's up";
  console.log(b);
  
  for (var i = 0; i < 5; i++) {
    // Do stuff that doesn't matter for this example...
  }
  
  // 😳Wait what? Even though variable i was declared in the loop...
  console.log(i); // ...and we're done with the loop, the console prints: 5
}

console.log(a);
console.log(b);

 

As you can see, our variable i which was declared and initialized within the context of the for loop is still accessible AFTER THE LOOP HAS FINISHED EXECUTING!

At first this made no sense to me. But as a I read the docs, I understood that this behavior is allowed because var is function-scoped. This is where hoisting comes in, I’ll explain.

In Javascript, hoisting means that variable declarations are processed before any code is executed. This means that the compiler will first identify all the variables that are declared within a scope before it goes on to execute the rest of the code and will assign to them a default value of undefined.

In doing so it’s as if you’ve declared all of your variables at the top of the scope. To the compiler, our code above looks more like this:

 

var a = 'hello';

function doSomething() {
  
  var b; // Initialized with the default value of: undefined
  var i; // Initialized with the default value of: undefined
  
  var b = "what's up";
  console.log(b);
  
  for (var i = 0; i < 5; i++) {
    // Do stuff that doesn't matter for this example...
  }
  console.log(i);
}

console.log(a);
console.log(b);

As you can see, all vars declared within the function (b and i) are first declared at the top of the function’s body. Why is it called hoisting then? Think of the vars inside the scope as a flag. The compiler takes those variables (which make up the flag) and hoists them up to the top of the scope, much like you would hoist up a flag to the top of a flagpole.

 

So since these variables are actually implicitly declared by the compiler at the beginning of the function it makes sense that we can access them anywhere within the function, including after the for loop.

Now where does that leave us with varlet, and const?

let only has one scope: the block scope. Contrary to var, it’s limited in scope to the block, statement, or expression on which it is used.

Going back to our perimeter analogy, we can say that its scope perimeter is drawn by the curly braces {} that surround it. For example, an if statement or a for loop would create a new block scope.

So if we take our code example and replace our var with a let in the forloop, we get this:

 

var a = 'hello';

function doSomething() {

  var b = "what's up";
  console.log(b);
  
  for (let i = 0; i < 5; i++) {
    // Do stuff that doesn't matter for this example...
  }
  
  console.log(i); // ❌error: Uncaught ReferenceError: i is not defined
}

console.log(a);
console.log(b);

Since let is block-scoped, and i is declared and initialized by the for loop, if we try to console log i after the loop has finished executing, we’ll get an error. I like this behavior much better since it’s more like other languages I’m used to and is less confusing to think about. In programming (and actually most of life), less confusing means less errors.

Now where does that leave constlet and const are similar in that constcan be block-scoped so all the above rules that we outlined for let also apply to const.

The difference between the two lies in part with their assignment.Assignment is the last term I mentioned was important to grasp at the beginning of this post, you’ll see why. Assignment is simply giving a value to a variable.

// Here we're assigning a value of 5 to our "number" variable.
let number = 5;

With that in mind, re-assignment simply means that you can assign a new value to an existing variable.

// Here we're assigning a value of 5 to our "number" variable.
let number = 5;
// And now we're re-assigning a value of 10.
number = 10;

 

Re-assigning is completely allowed with let, but absolutely illegal with const. In case you didn’t already guess, const was appropriately named after the term “constant”, which is a value that doesn’t change.

For example, this would throw an error:

const a = 'hello';
a = 'hello there!'; // ❌error: Uncaught Error: "a" is read-only

Another difference is that a const can be declared globally in your file, like a var, while a let can only be block-scoped.

This table summarizes what you’ve learned so far:

That’s all fine and dandy, but when should you use which keyword when you’re writing code?

It’s pretty much been accepted across the board that var is a thing of the past. You may stumble upon old code that still uses it but if you’re writing new code, it’s best not to use var.

The reason why is that it’s easier to make mistakes related to scope with var. Take this example:

function doSomething() {
  
  var number = 5;
  
  if (1 > 0) {
    var number = 1000;
    console.log(number); // The console prints: 1000
  }
  
  console.log(number) // 🤨The console prints: 1000
}

 

We initialize a variable at the beginning of our function. Later, in an ifstatement, we think we’re declaring a new variable limited in scope to the statement, but since var is function-scoped we’re actually inadvertently redeclaring our initial variable that has the same name.

This may seem easy to avoid since our example is really simplified, but imagine having to deal with a much more complex function with different nested blocks and statements or better yet, having to work with someone else’s code that you aren’t yet familiar with. Things can get messy real fast.

Because let and const have better scope management, it’s advised to use them instead of var. That’s right, you can just go ahead and forget about varwhen you’re writing new code 🤠

Now deciding between let and const pretty much just comes down to whether you want to be able to re-assign your variable or not. You can think about this in two ways.

You can decide to always use const unless you know that the value is going to change.

Or always use let unless you’re sure that you are going to be dealing with a constant.

So there you have it. You should now be better equipped to choose when to use varlet or const when writing JavaScript. I hope this blog post has cleared things up for you.

I would add that whenever you’re trying to learn a new topic in programming, you should try and take a look at the docs written on that topic by a trusted source. I like to look through Mozilla’s MDN Web Docs. They provide thorough explanations for all the terms and topics that you could think of in Javascript and other web languages.

Related Articles