Enea Xharja

Software Engineer

Variable Declarations: var, let, const

In JavaScript there are three ways to declare variables: var, let and const. To understand the differences between them, you need to know concepts such as variable declaration and initialization, scope and hoisting.

TL;DR

var

  • function scope
  • undefined when accessing before declaration

let

  • block scope
  • ReferenceError when accessing before declaration

const

  • block scope
  • ReferenceError when accessing before declaration
  • reassignment is not allowed

Variable Declarations and Initialization

To understand the concept of variable declaration and initialization, let us first clarify the difference between a variable and a value.

The most fundamental concept in JavaScript is the value. The JavaScript universe contains several values, and when I write code, I refer to these values and interact with them.

A variable is not a value. A variable is like a wire that points to a particular value. We can change the value it points to by using the assignment operator =.

For example, when you write var greeting = "hello" you create a variable called greeting and make it point to the value "hello".

There are two rules to follow when using the = assignment operator:

  • the left side of the assignment must be a variable (or wire)
  • the right side of the assignment must be an expression (or question you ask to JavaScript) that results in a value.

Variable declaration is the process of creating a variable. Variable initialization is the process of pointing a variable to a value for the first time.

var greeting; // -> Declaration
console.log(greeting); // undefined
greeting = "hello"; // -> Initialization
console.log(greeting); // hello

In JavaScript, when you declare a variable, it is automatically initialized with the value undefined. The previous example can be rewritten as follows:

var greeting = undefined; // -> Declaration and Initialization
console.log(greeting); // undefined
greeting = "hello"; // -> Reassignment
console.log(greeting); // hello

Scope

Scope is the context in which variables and functions are accessible inside a program.

Variables declared with var inside a function are accessible only inside that function or any nested functions. This is known as function scope.

function sayHello() {
  var greeting = "hello";
  return greeting;
}

sayHello(); // hello
console.log(greeting); // ReferenceError: greeting is not defined

In the above example, the variable greeting is declared inside the sayHello function and is not accessible outside it.

However, the variable greeting can be accessed inside the function and any nested functions.

function sayHello() {
  var greeting = "hello";

  function formatGreeting() {
    return greeting.toUpperCase();
  }

  return formatGreeting();
}

sayHello(); // HELLO
console.log(greeting); // ReferenceError: greeting is not defined

Hoisting

Hoisting refers to the process of initializing variables with the value undefined when they are created. This process occurs during the creation phase of the execution context.

Variables declared with var are hoisted to the top of their scope. This means that a variable can be accessed before it is declared.

Let's take the previous example and see how hoisting affects it.

function sayHello() {
  console.log(greeting); // undefined
  var greeting = "hello";

  function formatGreeting() {
    return greeting.toUpperCase();
  }

  return formatGreeting();
}

sayHello(); // HELLO

In the above example, the variable greeting is accessed before it is declared. This is possible because of hoisting. During the creation phase of the execution context, the variable greeting is initialized with the value undefined and then assigned the value "hello".

function sayHello() {
  var greeting; // -> Hoisting (Declaration and Initialization with undefined)
  console.log(greeting); // undefined
  greeting = "hello"; // -> Assignment

  function formatGreeting() {
    return greeting.toUpperCase();
  }

  return formatGreeting();
}

sayHello(); // HELLO

var and let in Comparison

The main difference between var and let is that the variables declared with let are block-scoped. This means that they are accessible only within the block in which they are declared. A block is anything wrapped in curly braces {}, such as a loop or conditional statement.

if (true) {
  let greeting = "hello";
}

console.log(greeting); // ReferenceError: greeting is not defined

In the above example, the variable greeting is declared inside the block of the if statement and is not accessible outside it.

The other difference between var and let is that variables declared with let are not hoisted. This means that a variable cannot be accessed before it is declared.

If you try to do this, instead of getting undefined, you will get a ReferenceError.

function sayHello() {
  console.log(greeting); // ReferenceError: Cannot access 'greeting' before initialization
  let greeting = "hello";

  function formatGreeting() {
    return greeting.toUpperCase();
  }

  return formatGreeting();
}

let and const in Comparison

The main difference between let and const is that variables declared with const cannot be reassigned. This means that once a variable is initialized with a value, it cannot be reassigned to a different value.

const greeting = "hello";
greeting = "hi"; // SyntaxError: Identifier 'greeting' has already been declared

In the previous example, the variable greeting is initialized with the value "hello". When you try to reassign it to the value "hi", you get a SyntaxError.

However, it is important to note that the value of a variable declared using const can still be mutated (changed).

const person = {
  name: "Mario",
};

person.name = "Luigi";
console.log(person); // { name: "Luigi" }

In the previous example, the variable person points to an object value. Although we cannot point person to a different value (no reassignment), we can still mutate the name property of the object.

One last thing to note about const is that it must be initialized when it is declared. If you try to declare a const variable without initializing it, you will get a SyntaxError.

const greeting = "hello"; // OK
const greeting; // SyntaxError: Missing initializer in const declaration

Conclusion

In JavaScript there are three ways to declare variables: var, let, and const. The main differences between them are:

  • var is function-scoped and hoisted (undefined when accessed before declaration)
  • let is block-scoped and not hoisted (ReferenceError when accessed before declaration)
  • const is block-scoped, not hoisted, and cannot be reassigned

ECMAScript 2015 introduced let and const to address the issues with var, while newer versions of ECMAScript have continued to improve the language by introducing new features and syntax.

Personally, I prefer to avoid using var altogether because of its hoisting behavior. I choose to use const by default and let only when I need to reassign a variable.