Variables

Var

Before ES6, the way to declare a variable was using the var keyword.

var foo = 'bar'

Not only the reference of a var variable can be changed but the variable itself can be redeclared.

var foo = 'bar'
foo = "rab"
foo // => "rab"
var foo = 42
foo // => 42

Function Scoping of Var

Variables declared with the var keyword are function scoped therefore - unless being declared in the global scope - they are only available within the scope of functions in which they are declared.

var foo = 'bar'

function logFoo() {
  var foo = 'rab'
  console.log(foo)
}

console.log(logFoo()) // "rab" 
console.log(foo) // "bar"

if (true) {
  var foo = '42'
}

console.log(foo) // 42

Hoisting Var

var variables - irrespective of where in a given scope are declared - are hoisted before code execution i.e. moved to the beginning of the scope and initialized with a value of undefined.

function logVar() {
  console.log(foo)
  var foo = 'bar'
  console.log(foo)
}

logVar() // undefined "bar"

Let

In addition to var ES6 introduced let and const keywords. let allows for defining variables, and const for defining constants.

Reference of both - a var variable and a let variable - can be changed but the let variable - as opposed to the var variable - cannot be redeclared.

let foo = 'bar'
foo = 'rab'
foo // => "rab"
let foo = 42 // Uncaught SyntaxError: Identifier 'foo' has already been declared

Block (aka Lexical, aka Static) Scoping of Let

A variable declared with let is not function scoped like var but block scoped. It means that a separate scope for let variable does not only exist within a global scope, within a function scope but also within a block scope.

let foo = 'bar'

if (true) {
  let foo = 'rab'
}

console.log(foo) // "bar"

Const

ES6 introduced the possibility of declaring constants with a const keyword.

Not only constants cannot be redeclared but also their references cannot be changed. Although, a reference of a once declared constant cannot change that does not mean that the referenced value is not allowed to change.

const foo = 'bar'
foo = 'rab' // Uncaught TypeError: Assignment to constant variable.

const foobar = { a: 1 }
foobar.a = 2
foobar // { a: 2 }

Constants - just like variables declared with let - are lexically scoped.