Think of let as a whiteboard with a label on top. You can erase what's under the label and write something new. const is a name carved into a trophy. Once it's carved it stays, and you can't swap it for a different trophy.
let: the whiteboard
Use let when a value needs to change over time, like the score in a game. You create the variable once, then give it new values with a plain =.
let score = 5;
console.log(score); // prints: 5
score = 10;
console.log(score); // prints: 10
You only write let the first time. After that the name already exists, so you just assign to it.
const: the trophy
A const gets one value and keeps it. Try to give it a new one and JavaScript stops with a TypeError.
const name = "Bitsy";
name = "Bob"; // TypeError: Assignment to constant variable.
A const also needs its value right away. Writing const age; on its own is a SyntaxError (Missing initializer in const declaration). Write const age = 7; instead.
So why have both? A const is a promise that this name will never get a new value. When you read your code later, or someone else does, there are fewer surprises. If you see const, you don't have to hunt for the line where it changes.
Block scope
Both let and const only exist inside the curly braces { } where you made them. The code between a pair of braces is called a block, and this rule is called block scope. An if, a loop or a plain pair of braces each make a block.
const pet = "cat";
if (true) {
let snack = "cookie";
console.log(pet, snack); // prints: cat cookie
}
console.log(pet); // prints: cat
pet was made outside the block, so it works in both places. snack only lives inside the braces. Adding console.log(snack) after the closing brace would stop the program with ReferenceError: snack is not defined.
This is helpful. A name you only need for a few lines disappears when you're done with it, so it can't clash with another name somewhere else in your program.
The twist: a const array can still change inside
A const locks the name. It doesn't lock what the name points to. An array is a list in square brackets, and if you make one with const you can still add to it, remove from it or change its items. What you can't do is point the name at a whole new array.
const toys = ["teddy"];
toys.push("robot");
console.log(toys); // prints: [ 'teddy', 'robot' ]
Writing toys = ["ball"]; would throw the same TypeError as before. Objects work the same way, so the properties of a const object can still change.
If you want the inside locked too, use Object.freeze. After that, push throws an error and the array stays as it was.
const snacks = Object.freeze(["apple"]);
try {
snacks.push("cookie");
} catch (err) {
console.log(err.name); // prints: TypeError
}
console.log(snacks); // prints: [ 'apple' ]
Object.freeze only locks the top level. An object stored inside a frozen array can still change.
Rule of thumb
Default to const, and use let only when the value must change.
Starting every variable with let out of habit. Write const first and switch to let only when you really need to give the name a new value, like a counter that goes up.
Check yourself
A const name can't be pointed at a new array. Pushing into the same array is fine, and let allows a new value.