Why ‘var’ Is Dangerous and How let/const Solve Its Pitfalls
This article examines why the traditional JavaScript var keyword should be avoided, explains the subtle pitfalls of its function scope, hoisting, and global leakage, and highlights the essential, often overlooked features of let and const—including temporal dead zone, true block scope, and immutability nuances.
Currently, let and const have replaced var, bringing better scope rules and stricter usage, yet even experienced developers overlook subtle details.
var problems: why not use it
Before diving into let and const, we need to understand why we shouldn't use var:
Function scope instead of block scope
<code>if (true) {
var x = 10;
}
console.log(x); // 10, variable x leaks to outer scope</code>Hoisting confusion
<code>console.log(x); // undefined, not error
var x = 5;
</code>Allow duplicate declarations
<code>var user = "张三";
var user = "李四"; // no error, silent overwrite
</code>Global declarations become properties of the global object
<code>var global = "我是全局变量";
console.log(window.global); // "我是全局变量" (browser)
</code>These characteristics cause many hard‑to‑track bugs, especially in large applications.
let core features: overlooked details
1. Temporal Dead Zone (TDZ)
This is probably the most easily missed feature of let:
<code>console.log(x); // ReferenceError: x is not defined
let x = 5;
</code>Unlike var, a variable declared with let exists in a "temporal dead zone" from the start of the block until its declaration, making it inaccessible.
<code>let x = 10;
function example() {
// x is in TDZ here
console.log(x); // ReferenceError
let x = 20; // x leaves TDZ
}
</code>Even if an outer scope already has a variable with the same name, the inner TDZ still prevents access.
2. True block scope
let strictly follows block‑scope rules, which is often underestimated:
<code>for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// output: 0, 1, 2
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// output: 3, 3, 3
</code>Each loop iteration creates a new binding for let, which is crucial when dealing with closures.
3. Does not pollute the global object
Although let does not become a property of the global object, a subtle detail is often missed:
Global let variables are stored in a special "script scope" environment rather than on the global object.
const core features: misunderstood immutability
1. Objects and arrays are still mutable
Many developers mistakenly think that const‑declared objects or arrays are completely immutable:
const only guarantees that the binding cannot be reassigned; the contents can still change. To create an immutable object, use Object.freeze():
Note that Object.freeze() performs only a shallow freeze.
Deep freezing requires recursively applying Object.freeze().
2. Must be initialized at declaration
The timing of initialization is often overlooked:
Although a const variable cannot be reassigned, its contents are not immutable.
3. Performance considerations
In some JavaScript engines, const declarations can offer slight performance benefits:
The engine can treat these values as never changing, enabling optimizations such as constant folding.
Practical usage patterns and best practices
1. Default to const, fall back to let when needed
This approach minimizes variable reassignment, improving readability and maintainability.
2. Destructuring with let and const
Function parameter destructuring is essentially a const declaration and cannot be reassigned.
3. Loops: let vs const
Using const in for‑of and for‑in loops is valid because each iteration creates a new binding.
Deep dive: internal mechanics of let and const
Understanding how JavaScript engines handle these declarations helps avoid common traps:
<code>// Simplified internal processing flow
function example() {
// 1. Create lexical environment
// 2. Mark let/const declarations as "uninitialized" (TDZ starts)
// console.log(x); // Uncommenting would throw: x is in TDZ
let x = 10; // x leaves TDZ and is assigned 10
if (true) {
// Create new block lexical environment
const y = 20;
x = 30; // Can access outer x
// y is only available in this block
}
// console.log(y); // Uncommenting would throw: y is not in this scope
}
</code>The distinction between Lexical Environment and Variable Environment determines how the engine treats different declaration types.
JavaScript
Provides JavaScript enthusiasts with tutorials and experience sharing on web front‑end technologies, including JavaScript, Node.js, Deno, Vue.js, React, Angular, HTML5, CSS3, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.