Understanding `var`, `let`, `const`, and No Keyword Variable Declarations in JavaScript

When working with JavaScript, understanding how variables are declared is crucial to writing clean, bug-free code. JavaScript provides multiple ways to declare variables: using var, let, const, or even without any keyword. Each has different behaviors, scoping rules, and best-use cases.

This article breaks down the key differences between them, providing real-world examples, potential pitfalls, and best practices.

No Keyword Variable Declaration

When you assign a value to a variable without declaring it with var, let, or const, JavaScript treats it differently based on whether strict mode is enabled.

Behavior in Non-Strict Mode

If the variable has not been declared previously, JavaScript assigns it as a property of the global object (window in browsers, global in Node.js).

x = 10; // No keyword
console.log(x); // 10
console.log(window.x); // 10 (in browsers)

This approach is not recommended because:
– It makes debugging harder, as undeclared variables can accidentally pollute the global scope.
– It can lead to unexpected behavior when multiple scripts are running.

Behavior in Strict Mode

In strict mode, undeclared variables throw an error instead of being automatically assigned to the global object.

"use strict";
x = 10; // ReferenceError: x is not defined

Key Takeaways

✅ Avoid using variables without a declaration keyword.
✅ Always declare variables explicitly with var, let, or const.


var: The Legacy Variable Declaration

Before ES6 (2015), var was the only way to declare variables in JavaScript. It still works today, but it comes with some drawbacks.

Characteristics of var

  • Function-scoped: It is accessible anywhere within the function where it is declared.
  • Hoisted: It is moved to the top of its function scope during execution.
  • Can be redeclared: You can declare the same variable multiple times in the same scope.

Example of var Scope Issue

Consider the following loop:

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i); 
  }, 1000);
}

Output:

3
3
3

This happens because var is function-scoped, meaning all setTimeout callbacks refer to the same i, which has already reached 3 when they execute.

Fixing It

A workaround using an IIFE (Immediately Invoked Function Expression):

for (var i = 0; i < 3; i++) {
  (function (j) {
    setTimeout(() => {
      console.log(j);
    }, 1000);
  })(i);
}

Use let instead (see next section).


let: Block-Scoped and More Predictable

Introduced in ES6 (2015), let solves the scoping issues of var.

Characteristics of let

  • Block-scoped: It is accessible only within the block {} where it is declared.
  • Hoisted but in the Temporal Dead Zone (TDZ): It cannot be accessed before its declaration.
  • Cannot be redeclared in the same scope.

Example of let Fixing the var Issue

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

Output:

0
1
2

Since let is block-scoped, each loop iteration has a new i variable, solving the issue.


const: Block-Scoped and Immutable References

const is also introduced in ES6 and should be used for values that should not be reassigned.

Characteristics of const

  • Block-scoped (same as let).
  • Hoisted but in the Temporal Dead Zone.
  • Cannot be reassigned after declaration.

Example

const name = "John";
name = "Doe"; // TypeError: Assignment to constant variable

However, objects and arrays declared with const can be mutated:

const user = { name: "John" };
user.name = "Doe"; // Allowed
console.log(user.name); // Doe

user = {}; // TypeError: Assignment to constant variable

Use const as the default unless reassignment is required.


Hoisting: var vs let vs const

All three are hoisted, but behave differently:

console.log(a); // undefined
var a = 5;

console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 10;

console.log(c); // ReferenceError: Cannot access 'c' before initialization
const c = 15;
  • var is hoisted but initialized as undefined.
  • let and const are hoisted but remain in the Temporal Dead Zone (TDZ) until their declaration.

Best Practices Summary

Feature var let const
Scope Function-scoped Block-scoped Block-scoped
Hoisting Yes (initialized as undefined) Yes (TDZ applies) Yes (TDZ applies)
Redeclaration Allowed Not allowed Not allowed
Reassignment Allowed Allowed Not allowed
Use Case Avoid When reassignment is needed Default for all variables

Use const by default.
Use let when reassignment is necessary.
Avoid var unless necessary for legacy support.
Never declare variables without a keyword.


Conclusion

Understanding the differences between var, let, const, and undeclared variables helps write cleaner, more maintainable JavaScript. By following best practices:

  • Prefer const for all variables unless you need reassignment.
  • Use let if reassignment is required.
  • Avoid var due to scoping issues.
  • Never declare variables without a keyword.

By mastering these concepts, you can prevent bugs, write more predictable code, and improve your JavaScript skills.

📌 Next Steps

  • Try rewriting old var code using let or const.
  • Experiment with hoisting and the TDZ to understand their effects.
  • Check out MDN’s documentation on let for more details.

Got questions? Drop them in the comments! 🚀

Leave a Reply

Your email address will not be published. Required fields are marked *