When we learn a programming language there are some basic concepts, like variable declaration, so simple that we can start use ’em almost without any knowledge about their functioning.

Now, I know what are thinking, “Why are you wasting time writing about that ?”

Because I think it’s worthwhile to spend some minutes to understand these elements in depth, what is their meaning and how should we use them. This will let you avoid some common mistake and write a better code.

In this article we will focus on how to store data in JavaScript.

Var

“A long time ago in a galaxy far far away…” the only option for defining variables in JavaScript was to use the var keyword and this was all you need to manage data in your code. But with the introduction of ES6 standard some oddities and lacks of the old JS became pretty clear:

  1. Scope: var keyword let you define variables only in global and local scope (or function scope). It doesn’t matter how many nested blocks of code you have, there are only these two possibilities.
  2. Constants: if you want to define something that won’t change during code execution you can rely only on the common sense of the developers. We can use some helpful conventions to make meaning clear (like uppercase naming), but there are no code constrains to guarantee that the value won’t change.
  3. Variable redeclaration: you can repeat the same variable declaration as many times as you want (in the same scope) and that’s a bit confusing if you want to maintains variables unique.

Definitions

Before starting with technical stuff let’s introduce some generic definitions:

  • Variable is a “container” which hold reusable data (very trivial).
  • Identifiers are the names of variables (very trivial too).
  • Block is a piece of code delimited by a pair of curly brackets (ref.), i.e. if, for, function, etc.
  • Scope determines the visibility of variables inside the code. If you have any doubts ask yourself: “Where is visible my variable in the code?”
    NOTE: Please don’t confuse scope with the execution context that is something different.
  • **Context (or Execution context) **is the environment where JavaScript code is executed, to keep things simple we can say that context is the object that own the code and this keyword is a reference to it. So ask yourself: “Which object is referenced by this?"

Now suppose that our developer is very passionate about Star Wars (that is good) but is suspicious about new standards like ES6 (that is bad) even though it’s been around for a while now. So he prefers writing his code in the old style of ES5 and it would look like this:

console.log("I am a %s", jedi);

var jedi = "Ani";

// print jedi variable before defining it
// => I am undefined

function useTheForce(comeToTheDarkSide) {
  var jedi = "Obi-Wan Kenobi";
  var jedi = "Anakin Skywalker";

  if (comeToTheDarkSide) {
    var jedi = "Darth Vader";
    console.log("I am %s", jedi);
    // => I am Darth Vader
  }

  console.log("I am %s", jedi);
  // I am Darth Vader
}

useTheForce(true);

console.log("I am %s", jedi);
// => I am Ani

As you can see there are three blocks* of code (including the global one) but only two scopes. That’s because the code inside the if *brackets doesn’t generate a scope. The console will output “I am Darth Vader” two times and then “I am Ani” in the global scope.

Please also note that the same variable is declared two times in a row inside the function and then another time in the if statement. That means that we have the same variable declaration in the same scope three times and any exception is thrown.

Last but not least is the output of first log: we are printing the value of our variable before defining it. That’s perfectly legal with var and is called hoisting.

Hoisting suggests that variables and function declarations are physically moved to the top of your code. Technically, what happens is that the variable and function declarations are put into memory during the compilation phase but stay exactly where you typed them in your code. The primary importance of hoisting is that it allows you to use functions before you declare them in your code.

You can read more in this article.

In our example the “jedi” variable declaration is put in memory and initialised with its default value (undefined).

Let

At this point, our developer understands that ES6 isn’t so bad and he decides to give let a chance:

console.log("I am a %s", jedi);

let jedi = "Ani";

// print jedi variable before defining it
// => Uncaught ReferenceError: Cannot acces 'jedi' before initialization

function useTheForce(comeToTheDarkSide) {
  let jedi = "Obi-Wan Kenobi";
  let jedi = "Anakin Skywalker";

  // => Uncaught SyntaxError: Identifier 'jedi' has already been declared

  if (comeToTheDarkSide) {
    var jedi = "Darth Vader";
    console.log("I am %s", jedi);
    // => I am Darth Vader
  }

  console.log("I am %s", jedi);
  // I am Anakim Skywalker
}

useTheForce(true);

console.log("I am %s", jedi);
// => I am Ani

But soon he realises that he can’t just change the keyword var to let, there are some fixes to do:

  1. hoisting doesn’t work in the same way: the variable is put in a state called Temporal Dead Zone* *and is not initialised until the definition is evaluated. What happens in our example is that a reference error is thrown.
  2. re-assignation is forbidden, variable definition must be unique in the scope. When you try a SyntaxError is thrown.
  3. if statement is a valid block scope so the “jedi” declaration inside it is unique.

Now the code should be like this:

let jedi = "Ani";
console.log("I am a %s", jedi);

// print jedi variable before defining it
// => I am Ani (before defining it) : 0

function useTheForce(comeToTheDarkSide) {
  let jedi = "Obi-Wan Kenobi";
  let jedi = "Anakin Skywalker";

  if (comeToTheDarkSide) {
    var jedi = "Darth Vader";
    console.log("I am %s", jedi);
    // => I am Darth Vader
  }

  console.log("I am %s", jedi);
  // I am Anakim Skywalker
}

useTheForce(true);

console.log("I am %s", jedi);
// => I am Ani

Const

Now that you know everything about let is simple introduce const keyword. Basically we can say that is like let but you can’t reassign another value. You need also to know that assignation is allowed only during const declaration.

function useTheForce(comeToTheDarkSide) {

  const jedi = "Yoda";

  if (true) {
    jedi = "Darth Yoda";
    console.log("I am %s", jedi);

    // => TypeError: Assignement to constant variable.
  }

}

useTheForce(true);

Supposing that in our example “jedi” is a const with value “Yoda”, if we try to change the value inside the if statement it will throw a TypeError, and this makes sense as Yoda would never join the dark side.

Conclusion

In natural languages when is impossible to express a concept with a word, a new one is introduced to fill the gap. This is true in English (ref.), Italian, Esperanto, Ewokese (I suppose), etc. This is even more true in programming languages like JavaScript.

Now you know that you can:

  • declare variables at block scope and allocate memory for them only when a block is evaluated
  • keep variable declarations unique inside a scope, avoiding that a developer by mistake could override a variable used in another part of the code (with another meaning)
  • be sure that a variable stored in a const won’t change at any time.

My final suggestion is to use const as default choice. When you need to reassign a variable use **let **(like in loops). And use var when... no you don’t need var anymore, really

Did you like this article? Consider becoming a Patron!

This article is CC0 1.0 (Public Domain) licensed.