Sébastien TIMONER
Expert in web development and technical team management, I specialize in creating and optimizing high-performance digital solutions. With deep expertise in modern technologies like React.js, Node.js, TypeScript, Symfony, Docker, and FrankenPHP, I ensure the success of complex SaaS projects, from design to production, for companies across various sectors, at offroadLabs.
Hey there, you JavaScript developer pulling your hair out over these variables! Wondering when to use var, let, or const without embarrassing yourself in team meetings? Stay put, we'll sort this out together, calmly and with a smile. On the agenda: we'll explore each keyword, compare their scope (global, function, block), have a laugh about hoisting (that somewhat magical JavaScript mechanism), throw in some practical examples, and finish with a dash of TypeScript. Ready? Let's go!
Let's start with var, the elder of JavaScript variables. If JavaScript were a TV series, var would be that old-school character, unpredictable but endearing. Until ES5, it was all we had to declare our variables. You can still use it today, but beware: it has some quirks that might surprise you.
var x = 1; var x = 2;
won't trigger an error – JavaScript will just overwrite the first value with the second. Handy for legacy code, but a source of confusion to avoid in new code.To illustrate var's overly broad scope, here's a small example:
javascript
Yes, even though this fruit was declared inside an if statement, the console.log outside the block displays it without any problem. var doesn't care about block boundaries: it's either global or attached to its containing function, period.
And the best (or worst) for last: hoisting. In English, we sometimes call it "lifting," but let's be honest, we mostly use the term hoisting. It's var's tendency to quietly declare itself at the top of the scope. JavaScript, during execution, acts as if all var declarations were "lifted" to the top of the function. As a result, you can use your variable before its declaration line... with a somewhat strange default value. Let's look at this more closely in the hoisting section below (spoiler: var loves to surprise you with undefined).
In summary, var has served us well, but it's a bit sneaky around the edges: broad scope, silent re-declaration, unpredictable hoisting... We can do better, and luckily, ES6 introduced new keywords to bring order to the chaos!
In 2015, JavaScript welcomed let (along with const). And there, we got a little variable makeover. let is like the cool, disciplined kid: it fixes most of var's quirks while remaining flexible.
let x = 1; let x = 2;
in the same block, you'll get a nice SyntaxError: Identifier 'x' has already been declared
. That avoids a lot of confusion, admit it.To clearly see the scope difference compared to var, let's compare with the previous example by replacing var with let:
javascript
With let, once you're out of the if statement's curly braces, that's it, the vegetable variable is no longer defined. Try to log it and bam: ReferenceError. That's let enforcing good scoping behavior.
And what about hoisting? let variables (and const) are also hoisted, but differently. They're not usable before being declared. Technically, the JS engine knows they exist in the block (it reserves the memory), but until the declaration line is executed, any attempt to access them will trigger an error. We say these variables are in the Temporal Dead Zone (yes, it sounds like a horror movie, but it's just the fancy name for "inaccessible before initialization"). We'll detail this behavior a bit later, but remember that let won't let you go wild using a variable too early.
In practice, let has become the default choice for variables that will change over time. It brings clarity (block scope) and safety (no accidental re-declaration, no access before declaration). In short, let is your new friend for declaring temporary or modifiable variables.
Now for const! Introduced at the same time as let, this keyword creates constants... or almost. Let's say it creates variables whose reference won't change. const has the same block scope as let (no more vars wandering around) and the same hoisting rules (temporal dead zone until declaration). The big difference is that once initialized, the variable can't be reassigned.
const x;
by itself won't work (direct SyntaxError). You need to do const x = 42;
for example.Look at this small example to clearly see the difference with a modifiable variable:
javascript
We define PI as constant, we can use it without worry as long as we don't try to change it. When we try to do PI = 3.15, JavaScript stops us cold: no modifying a constant.
Note: Constant doesn't mean 100% immutable. If the value is an object or an array, you can still modify the inside of the object/array. What doesn't move is the variable itself (the memory reference). For example:
javascript
Here, we could change name in the user object despite const. However, completely reassigning user to another object is forbidden. So, const = constant, not modifiable, but only at the variable level itself. Don't confuse variable constancy with value immutability.
javascript
With an array, it's the same logic: we can modify its content (add, delete, modify elements) because we're only changing the inside of the array. But we can't reassign the fruits variable to a new array. It's like the array is a box: we can change what's inside, but we can't replace the box itself.
In practice, const is ideal for all values that shouldn't change: for example, a configuration, a reference, etc. It's widely used and makes code more robust (we know this variable won't move). In fact, best practices often recommend using const by default, and switching to let only when we know the value will need to change. As for var... well, unless there's a specific reason, we forget about it 😉.
Let's talk more about scope. Scope determines "where" a variable is accessible in your code. There are mainly three levels of scope in JavaScript:
In summary: var is limited to the function (or global), while let and const are limited to the current block. This difference solves many classic pitfalls. For example:
javascript
Here, the loop with var leaves i hanging outside, with its final value 3. The loop with let, on the other hand, cleans up j on exit: impossible to access it afterward. In practice, this means you can have several let variables named the same in different blocks without interference, where a single var would have been shared between all these blocks.
Let's move on to hoisting, this declaration lifting mechanism. It's often a source of confusion (and jokes in meetings), so let's clarify. Hoisting is when the JavaScript engine "moves" variable and function declarations to the top of their scope during execution. In reality, nothing is really moved in your code, it's just that JavaScript allocates memory for your variables upfront, before executing step by step.
Let's do a little hoisting test in practice:
javascript
At the time of the first console.log, the myVar variable has indeed been created in memory via hoisting, but its default value is undefined (since we haven't assigned it yet). No error, just undefined being displayed. Then we assign "Hello", and the second time we get the expected value.
Now let's see with let:
javascript
Here, the first console.log triggers a ReferenceError. Why? Because myLet is hoisted without initialization. It's in its temporal dead zone, inaccessible. JavaScript refuses to give us a value that doesn't really exist yet. Once we execute let myLet = "Hello";
, the variable comes out of its dead zone and gets "Hello". The second console.log then works perfectly.
We can see it clearly: var behaves like a ninja by lifting its declaration to the top and quietly giving itself the value undefined, while let/const play it safe and prevent any use until they're initialized. Moral of the story: avoid coding by relying on hoisting, it's a recipe for brain knots (and unexpected undefineds). Better to declare your variables at the top of their scopes and keep your code clear. If you forget, let/const's behavior will remind you with an error, where var would have let you flounder with an undefined.
Ah, and while we're at it, note that classically declared functions (function myFunction() \{ ... \}
) are also fully hoisted. You can call a function declared lower in your code, JavaScript will already know it. But be careful, arrow functions or function expressions assigned to variables follow the hoisting rules of var/let according to the keyword used (a topic for another day 😉).
Before we part ways, let's quickly talk about TypeScript. If you start using TypeScript (the super-set of JavaScript that adds static typing), you'll find our three friends var, let, and const. Good news: their scope and hoisting behaviors remain exactly the same as in pure JavaScript, since TypeScript compiles to standard JavaScript. However, TypeScript brings its personal touch to typing, and it's worth a look, especially for let and const.
let fruit = "apple";
, TypeScript will infer that fruit is of type string. If you write let age = 25;
, it will understand that age is a number, etc. For let, it stays quite broad: the variable can change, so the type is the basic one (string, number, etc.).typescript
The color variable is of type "red" and not just string. Benefit? If later your code expects exactly the value "red" or "blue" (for example a parameter that can only be one of these colors), and you have const color = "red"
, you can pass it without worry. With a let color = "red"
(broad string type), TypeScript would have complained because "red" is just one possibility among all strings. In short, const in TypeScript allows for ultra-precise typing when it's useful. It's like a bonus that further reinforces the constant concept.
console.log(myVar); var myVar = 3;
, it will signal that you have a scope or order problem. Same for a let used too early, TS knows the hoisting rules and will protect you (as much as possible) from mistakes.In summary, TypeScript doesn't change the fundamental rules of var/let/const, but it reinforces them with the safety net of typing. You gain in clarity and catch errors earlier. Remember especially that const in TypeScript is doubly winning: a non-reassignable variable and a precise literal type – what more could you ask for?
We've covered everything, so who's the winner in the var vs let vs const match? No big surprise: in 2025, the let & const duo wins hands down for writing clean and maintainable code. Here's a spicy recap of practical advice:
There you go, you now know when to use var, let, or const in JavaScript, and even a bit about how it works with TypeScript. No more confusing our three companions or getting teased in code review because you pulled a var out of your hat without a valid reason. Go code peacefully, and don't forget: const as much as possible, let if necessary, and var... as little as possible! Happy coding!