ES6 Basics: var, const, and let

Recently I was asked about variable declarations in ES6 JavaScript. The person that asks me wasn't familiar with ES6 so I explained it a bit. After thinking through I think I glossed over the basics on this. I'll write my basic understandings on this topic.


So pre ES6 we only have var for declaring variables. In ES6, we have two other ways of declaring variables, using const and let. The first difference that came to my mind regarding var and the new variable declarations is that var is function scoped; const and let are block scoped (curly braces). Let's see the codes below:

function superHeroName() {
  var name = "spiderman"
  console.log(name) // this will work printing 'spiderman' in the console
console.log(name) // this will print empty string because of var hoisting

This would turn as expected the console.log(name) will not output 'spiderman' but an empty string. The var is declared and assigned in the superHeroName function so its value should not be available outside the function. The variable, however, is hoisted; declared in the global scope. Then how if we use var not in a function but in a block?

do {
  var family = "Ben Parker"
  console.log(family) // this will print 'Ben Parker' in the console
} while (false)

console.log(family) // this will also print 'Ben Parker' in the console, huh!?

This might not work as we expected. The var declaration/assignment in the do block might be intended only as a temporary variable inside the curly braces but it leak outside to the global scope.

IIFE (Immediately Invoked Function Expression)

We might be able to use IIFE (Immediately Invoked Function Expression) to prevent the variable polluting the global scope.

;(function () {
  do {
    var lover = "Mary Jane"
    console.log(lover) // this will correctly output 'Mary Jane'
  } while (false)

console.log(lover) // this will be not work

The console.log(lover) outside of the IIFE will return ReferenceError: lover is not defined. Signifies that the lover variable scoped in the IIFE and not in the global scope. This can circumvent the leaking of var but the code might not be the prettiest thing in the world 😏

const vs let

const and let are block scoped so when they are declared inside curly braces they can only be used there.

do {
  const power = "heightened spider sense"
} while (false)

console.log(power) // this will not work

The const in the above example properly scoped power variable inside the block. When we tried to console.log(power) outside of the block it will return ReferenceError: power is not defined.

So what is the difference of const and let? well the most basic thing is that const doesn't allow variable reassignment and let can reassign. Lets see an example here:

const bitten = "by spider"
console.log(bitten) // will output 'by spider'
bitten = "by elephant" // will not work

The bitten = 'by elephant' will not work and return error TypeError: invalid assignment to const 'bitten'. How about let?

let spiderman = "Peter Parker"
console.log(spiderman) // will output 'Peter Parker'
spiderman = "Miles Morales"
console.log(spiderman) // will output 'Miles Morales'

The reassignment will work as expected. So const is immutable? nope! when we assign a value toconst doesn't mean that we cant modify the assigned value but only the reference that is read-only. For example if we have an object as const we can't change the object to another value, but the we can modify the object properties. Lets see the code below:

const enemy = { name: "doctor octopus", status: "uncaught" }
console.log(enemy) // will output `Object { name: "doctor octopus", status: "uncaught" }`
enemy.status = "jailed"
console.log(enemy) // will output `Object { name: "doctor octopus", status: "jailed" }`

So when we want to do something like a for-loop we need to use let as it can be reassigned for each iteration, but const can't.

Temporal Dead Zone

One thing about hoisting is that all variables are hoisted, they are processed before any code executed. const and let are also hoisted, but they can't be used until they are declared. An example code:

console.log(costume) // will output 'undefined'
var costume = "red and blue"

console.log(finance) // cannot work
let finance = "poor"

console.log(smart) // cannot work
const smart = true

The console.log(finance) will return error ReferenceError: can't access lexical declaration 'finance' before initialization. The same one will also happen to console.log(smart). The finance and smart are actually hoisted, they are processed in the background. But they are in Temporal Dead Zone they can't be used before officially declared. To fix the above code we need to rewrite it as follow:

var costume = "red and blue"

let finance = "poor"
console.log(finance) // will output 'poor'

const smart = true
console.log(smart) // will output 'true'