You already mentioned one advantage: Block scope in loops. This avoids issues with defining functions inside a loop, which I'd argue is one of the biggest pitfalls for people new to JavaScript:
// With var
var elements = document.querySelectorAll('.var');
for (var i = 0; i < elements.length; i++) {
elements[i].onclick = function() {
console.log("I'm element #" + (i + 1) + ".");
};
}
// With let
elements = document.querySelectorAll('.let');
for (let i = 0; i < elements.length; i++) {
elements[i].onclick = function() {
console.log("I'm element #" + (i + 1) + ".");
};
}
<h3>With <code>var</code></h3>
<div class="var">Click me!</div>
<div class="var">Click me!</div>
<h3>With <code>let</code></h3>
<div class="let">Click me!</div>
<div class="let">Click me!</div>
Another advantage is that let
variables aren't initialized by default. This makes it easier to catch mistakes when trying to access a variable with the same name in a higher scope:
// With `var`, we get `undefined` which can be tricky to debug in more
// complex code.
var foo = 42;
function bar() {
console.log(foo); // undefined
var foo = 21;
}
bar();
// With `let`, we get a ReferenceError, which makes it clear that we made
// a mistake here.
var foo = 42;
function bar() {
console.log(foo); // ReferenceError
let foo = 21;
}
bar();
All in all this aligns JavaScript more with how other languages work.