Difference Between Var, Let, and Const in JavaScript | Interview Guide
Master the difference between var, let, and const in JavaScript. Learn about scope, hoisting, and the Temporal Dead Zone (TDZ) with real-world examples and interview questions.
Demystifying JavaScript Variables: The Ultimate Guide to Var, Let, and Const for Interviews
If you are stepping into a JavaScript interview, whether it is for a junior front-end role or a senior full-stack position, there is one question you are almost guaranteed to face: "Can you explain the difference between var, let, and const?"
On the surface, it feels like a simple question. You might think, "Well, var is old, let is new, and const cannot be changed." While that is functionally true, answering it that simply in an interview is a missed opportunity. Interviewers do not just want to know how to use them; they want to know if you understand how the JavaScript engine treats them under the hood. They are listening for keywords like Scope, Hoisting, Reassignment, and the dreaded Temporal Dead Zone (TDZ).
In this comprehensive guide, we are going to break down these three variable declarations like never before. We will look at how the JavaScript engine reads your code, explore real-world analogies, and tackle the exact trap questions interviewers use to test your true understanding.
Grab a cup of coffee, open your code editor, and let us dive deep into the world of JavaScript variables.
The Pre-requisites: Scope and Hoisting
Before we can accurately compare var, let, and const, we need to understand the environment they live in. If you do not understand Scope and Hoisting, the differences between these variables will just feel like arbitrary rules you have to memorize.
What is Scope?
Think of "Scope" as the visibility or lifespan of a variable. It dictates where in your code a specific variable can be accessed, modified, or seen. JavaScript traditionally had two types of scope, but ES6 (ECMAScript 2015) introduced a third.
- Global Scope: A variable declared outside of any function or block. It is visible to everything, everywhere in your JavaScript file. (Think of this as a public billboard).
- Function Scope: Variables declared inside a function are locked inside that function. The outside world cannot see them. (Think of this as a secure office building).
- Block Scope: A block is any code wrapped inside curly braces {}. This could be an if statement, a for loop, or just empty brackets. Block scope means a variable is locked specifically inside those brackets. (Think of this as a private meeting room inside the office building).
What is Hoisting?
Hoisting is often explained as "JavaScript moving your variables to the top of the file." But that is a myth. Nothing actually moves.
Instead, when you run a JavaScript file, the engine goes through a Memory Creation Phase before it executes a single line of code. It scans your document for variables and functions, and allocates memory space for them before running the code line by line. How var, let, and const react to this memory phase is one of their biggest differences.
Now that we have the foundation, let us tear down each variable type.
1. The Wild West: var
Before 2015, var was the only way to declare variables in JavaScript. It is old, it is unpredictable, and quite frankly, it has caused millions of hours of debugging headaches for developers around the world.
Scope: Function Scoped
The biggest defining feature of var is that it is function scoped, but it is not block scoped. If you declare a var inside an if statement or a for loop, it ignores those curly braces and leaks out into the surrounding function (or into the global scope).
Code Example:
JavaScript
function testVarScope() {
if (true) {
var secretMessage = "I am a secret!";
}
// We can still access it outside the 'if' block!
console.log(secretMessage); // Output: "I am a secret!"
}
testVarScope();
Why is this bad? Because if you have a massive file and you use var counter = 0 inside a loop, you might accidentally overwrite another counter variable you created somewhere else in the same function. It causes silent bugs that are incredibly hard to track down.
Hoisting: Initialized as undefined
When the JavaScript engine does its memory sweep (Hoisting), it spots var declarations, sets aside memory for them, and immediately assigns them a default value of undefined.
Because of this, you can actually try to log a var variable before you write the line of code that declares it, and your app will not crash.
Code Example:
JavaScript
console.log(companyName); // Output: undefined
var companyName = "Tech Solutions Inc.";
console.log(companyName); // Output: "Tech Solutions Inc."
Re-declaration and Re-assignment
var is completely reckless. You can re-assign its value, and worse, you can completely re-declare it in the same scope without JavaScript throwing an error.
JavaScript
var userAge = 25;
var userAge = 30; // No error! The engine just shrugs and overwrites it.
Real-World Analogy for var
Imagine var as the office gossip. You tell them a secret inside a private meeting room (a block), but the moment the door opens, they leak that information to the entire office floor (function scope). Furthermore, if someone tells them a different version of the story (re-declaration), they instantly forget the old one without telling anyone.
2. The Reliable Professional: let
When ES6 arrived in 2015, it brought let and const to fix the mess that var had created. let is the modern standard for variables that need to change over time (like counters, toggles, or dynamic strings).
Scope: Block Scoped
Unlike var, let respects curly braces. If you create a let variable inside an if block, a for loop, or a while loop, it stays trapped inside those braces. It is strictly block scoped.
Code Example:
JavaScript
function testLetScope() {
if (true) {
let secureMessage = "Top Secret!";
console.log(secureMessage); // Works perfectly here
}
// Trying to access it outside the block:
console.log(secureMessage); // ReferenceError: secureMessage is not defined
}
testLetScope();
This is a massive upgrade. It means developers can confidently name variables inside loops without fearing they will accidentally overwrite data globally.
Hoisting and The Temporal Dead Zone (TDZ)
This is the most critical part of an interview answer. Does let get hoisted? YES.
Many developers incorrectly say that let and const are not hoisted. They absolutely are. The JavaScript engine allocates memory for them during the creation phase. However, unlike var (which gets initialized with undefined), let and const remain uninitialized.
The time between the start of the block and the exact line of code where the variable is declared is known as the Temporal Dead Zone (TDZ). If you try to access the variable while it is in the TDZ, JavaScript will throw a strict error.
Code Example:
JavaScript
// Temporal Dead Zone starts here for 'projectName'
console.log(projectName); // ReferenceError: Cannot access 'projectName' before initialization
let projectName = "Apollo"; // TDZ ends here. Variable is initialized.
console.log(projectName); // Output: "Apollo"
The TDZ is a safety mechanism. It forces you to write clean, top-down code and prevents you from using variables before they hold actual data.
Re-declaration and Re-assignment
With let, you can re-assign the value later on. However, you cannot re-declare the same variable name in the same scope. This prevents accidental overwriting.
JavaScript
let score = 10;
score = 15; // Re-assignment is totally fine.
let score = 20; // SyntaxError: Identifier 'score' has already been declared
Real-World Analogy for let
Imagine let as a whiteboard in a private meeting room (block scope). Only the people inside that specific room can see and update the whiteboard. You can erase the whiteboard and write new numbers on it (re-assignment), but you cannot bring a second whiteboard into the exact same room and give it the exact same name (no re-declaration).
3. The Unbreakable Vow: const
const stands for "constant." You should use const by default for almost everything in modern JavaScript, unless you explicitly know the value needs to change (in which case, use let).
Scope and Hoisting
const behaves exactly like let in terms of scope and hoisting.
- It is Block Scoped (respects curly braces {}).
- It is Hoisted but placed in the Temporal Dead Zone, meaning you cannot access it before initialization.
Re-declaration and Re-assignment
Here is where const differs from let.
With const, you cannot re-declare it, and you cannot re-assign it. Once a primitive value (like a String, Number, or Boolean) is locked into a const variable, it is set in stone. Furthermore, you must initialize a const at the exact time you declare it.
Code Example:
JavaScript
// This will throw a SyntaxError immediately. You must assign a value!
const apiBaseUrl;
const pi = 3.14159;
pi = 3.14; // TypeError: Assignment to constant variable.
The Ultimate Interview Trap: Mutability vs. Re-assignment
Interviewers love this trap. They will ask: "Can you change the value of a const variable?"
If you say "No, never," you have fallen into the trap.
The correct answer is: It depends on the data type.
const prevents re-assignment of the memory address. If you use primitive types (strings, numbers), they cannot be changed. But if you assign an Object or an Array to a const, the reference to that object is locked, but the contents inside the object are highly mutable (changeable).
Code Example:
JavaScript
const userProfile = {
name: "Rahul",
role: "MERN Developer"
};
// This is perfectly legal! We are modifying the contents, not re-assigning the variable.
userProfile.role = "Senior Architect";
console.log(userProfile.role); // Output: "Senior Architect"
// This will fail! We are trying to point 'userProfile' to a brand new object.
userProfile = { name: "Amit" }; // TypeError: Assignment to constant variable.
If you truly want an object to be completely locked down and unchangeable, you have to use a specific JavaScript method called Object.freeze().
JavaScript
const config = Object.freeze({ theme: "dark" });
config.theme = "light"; // Fails silently, or throws error in strict mode.
Real-World Analogy for const
Think of const like a permanent ID Card lanyard given to an employee. You cannot give them a completely different lanyard (no re-assignment). However, if there is a clear plastic pouch on that lanyard (an Object/Array), you can easily take out the old ID paper and slide in a new one with an updated job title (mutating the contents).
The Ultimate Quick Reference Guide
If you need to quickly review before an interview, memorize this table.
FeaturevarletconstScopeFunction / GlobalBlock ({})Block ({})HoistingYes (Initialized to undefined)Yes (Uninitialized)Yes (Uninitialized)Temporal Dead Zone (TDZ)NoYesYesCan Re-declare?YesNoNoCan Re-assign?YesYesNoMust Initialize upon Declaration?NoNoYes
Real Interview Q&A Walkthrough
Let us look at a few advanced scenarios interviewers use to test your mastery of these concepts.
Interview Question 1: The Loop and the Timeout Trap
The Setup: An interviewer slides you this piece of code and asks, "What will this output to the console?"
JavaScript
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
How to Answer:
Most beginners guess 0, 1, 2. The correct answer is 3, 3, 3.
Here is why: var is function scoped, not block scoped. By the time the setTimeout finishes waiting for 1 second, the for loop has already finished running. The single variable i sitting in memory is now 3. All three timeouts look at the exact same variable i and print 3.
To fix this, simply change var to let. Because let is block scoped, a brand new memory binding is created for i every single iteration of the loop. If you use let, the output will correctly be 0, 1, 2.
Interview Question 2: Accessing variables before initialization
The Setup: "Can you explain the difference in error outputs between these two blocks of code?"
Block A:
JavaScript
console.log(username);
var username = "Alex";
Block B:
JavaScript
console.log(adminName);
let adminName = "Sarah";
How to Answer:
Block A will output undefined. Because var is hoisted and given a default value of undefined during the memory phase, the code does not crash.
Block B will throw a ReferenceError. Even though let is hoisted, it is caught in the Temporal Dead Zone. The engine knows adminName exists in the memory space, but strictly forbids you from touching it until the initialization line is executed.
Interview Question 3: Why should we stop using var entirely?
How to Answer:
"Using var introduces unpredictability into the codebase. Because it ignores block scope, it increases the risk of variable leaking and accidental overwriting, especially in large, complex applications. It also masks errors by returning undefined when hoisted, rather than throwing a hard error like let or const do via the Temporal Dead Zone. By sticking to const for immutability and let for variables that must change, we write predictable, safer, and cleaner code."
Final Thoughts for Your Interview
When you are sitting in that interview chair, remember that coding is not just about making things work; it is about writing predictable, maintainable software.
By understanding the deep mechanics behind var, let, and const, you are proving to the interviewer that you do not just memorize syntax. You are showing them that you understand the JavaScript engine, memory allocation, and the historical context of ECMAScript updates.
Embedded resource: youtube
A golden rule of thumb to take away: Always default to const. If you realize later that the variable needs to be updated (like a counter in a loop), change it to let. And as for var? Leave it in the past where it belongs.
Happy coding, and good luck on your next JavaScript interview!