JavaScript Variables, Constants, and Declarations: The Foundation of Data Storage
In the world of programming, we need places to store various data—just like using sticky notes for to-do items, notebooks for ideas, and labels for items in real life. In JavaScript, variables and constants play these roles as containers for storing data.
What are Variables?
A variable is like a labeled box where you can put things and also change the contents of the box. The label is the variable name, and the content inside the box is the variable value.
// Creating a variable is like preparing a box
let userName = "Emily";
// You can look at what's inside the box
console.log(userName); // "Emily"
// You can change what's inside the box
userName = "David";
console.log(userName); // "David"Three Declaration Methods: var, let, const
JavaScript provides three keywords for declaring variables. They are like three different types of containers, each with its own characteristics.
var—A Relic from the Past
var was JavaScript's earliest variable declaration method. Before ES6 (2015), it was the only choice. However, due to some issues, it's no longer recommended for use.
// Declare variables using var
var age = 25;
var city = "New York";
var isStudent = true;
console.log(age, city, isStudent); // 25 "New York" true
// var allows re-declaring variables with the same name (this is a problem!)
var age = 30; // No error, but easily causes confusion
console.log(age); // 30Main problems with var:
- Function scope: Variables are only valid within functions, no block-level scope
- Variable hoisting: Declarations are moved to the top of the scope, potentially causing confusion
- Can be re-declared: Variables with the same name can be re-declared, easily causing errors
// Problem example 1: No block-level scope
if (true) {
var message = "Hello";
}
console.log(message); // "Hello" - variable leaked outside!
for (var i = 0; i < 3; i++) {
// Loop body
}
console.log(i); // 3 - loop variable leaked!
// Problem example 2: Variable hoisting
console.log(fruit); // undefined - no error, but results are confusing
var fruit = "apple";
// The above code is actually interpreted as:
var fruit;
console.log(fruit);
fruit = "apple";let—Modern Variable Declaration
let is a new keyword introduced in ES6 that solves most of var's problems. It's like putting clear usage restrictions on boxes, making management more standardized.
// Declare variables using let
let userName = "Alice";
let userAge = 28;
let isActive = true;
// Variables declared with let can be modified
userName = "Bob";
userAge = 29;
isActive = false;
console.log(userName, userAge, isActive); // "Bob" 29 falseAdvantages of let:
- Block scope: Variables are only valid within the code block where they are declared
- Cannot be re-declared: Variables with the same name cannot be declared in the same scope
- Temporal Dead Zone: Variables cannot be used before declaration
// Advantage 1: Block scope
if (true) {
let message = "Hello";
console.log(message); // "Hello"
}
console.log(message); // ReferenceError: message is not defined
for (let i = 0; i < 3; i++) {
console.log(i); // 0, 1, 2
}
console.log(i); // ReferenceError: i is not defined
// Advantage 2: Cannot be re-declared
let score = 100;
let score = 200; // SyntaxError: Identifier 'score' has already been declared
// Advantage 3: Temporal Dead Zone (TDZ)
console.log(color); // ReferenceError: Cannot access 'color' before initialization
let color = "blue";const—Guardian of Constants
const was also introduced in ES6 for declaring constants. It's like a box sealed with super glue—once you put something in, you can't change it anymore.
// Declare constants using const
const PI = 3.14159;
const API_URL = "https://api.example.com";
const MAX_USERS = 100;
// Trying to modify const variables will cause an error
PI = 3.14; // TypeError: Assignment to constant variable
console.log(PI); // 3.14159Characteristics of const:
- Must be initialized: Must be assigned a value when declared
- Cannot be reassigned: Values cannot be changed
- Block scope: Same as let
- Object properties can be modified: If it's an object, object properties can still be modified
// Characteristic 1: Must be initialized
const name; // SyntaxError: Missing initializer in const declaration
const name = "John"; // Correct
// Characteristic 2: Cannot be reassigned
const age = 25;
age = 26; // TypeError: Assignment to constant variable
// Characteristic 4: Object properties can be modified (important!)
const user = {
name: "Sarah",
age: 25
};
// Cannot reassign the entire object
user = { name: "Tom", age: 30 }; // TypeError
// But can modify object properties
user.age = 26;
user.city = "London";
console.log(user); // { name: "Sarah", age: 26, city: "London" }
// Same applies to arrays
const numbers = [1, 2, 3];
numbers.push(4); // Can modify array contents
numbers[0] = 100; // Can modify elements
console.log(numbers); // [100, 2, 3, 4]
numbers = [5, 6, 7]; // TypeError: Assignment to constant variableHow to Use Object.freeze to Create True Constants
If you want to make an object completely immutable, you can use Object.freeze():
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
});
// Attempts to modify will silently fail (non-strict mode) or error (strict mode)
config.apiUrl = "https://new-api.example.com";
config.newProp = "value";
console.log(config);
// { apiUrl: "https://api.example.com", timeout: 5000 }
// No changes made
// In strict mode, it will error
("use strict");
const settings = Object.freeze({ theme: "dark" });
settings.theme = "light"; // TypeError: Cannot assign to read only propertyVariable Naming Rules
Naming variables is like naming pets—you need to follow some rules and also some established best practices.
Must-Follow Rules
// 1. Can only contain letters, numbers, underscores (_), and dollar signs ($)
let userName = "valid";
let user_name = "valid";
let $user = "valid";
let _user = "valid";
// 2. Cannot start with a number
let 1user = "invalid"; // SyntaxError
let user1 = "valid";
// 3. Cannot use JavaScript reserved words
let let = "invalid"; // SyntaxError
let const = "invalid"; // SyntaxError
let function = "invalid"; // SyntaxError
let if = "invalid"; // SyntaxError
// 4. Case-sensitive
let name = "John";
let Name = "Sarah";
let NAME = "Michael";
// These are three different variables!Best Practices
// 1. Use camelCase
let userName = "John";
let userAge = 25;
let isActive = true;
let getUserInfo = function () {};
// 2. Use uppercase + underscores for constants
const MAX_RETRY_COUNT = 3;
const API_BASE_URL = "https://api.example.com";
const DEFAULT_TIMEOUT = 5000;
// 3. Use meaningful names
// ❌ Bad naming
let x = "John";
let d = new Date();
let arr = [1, 2, 3];
// ✅ Good naming
let userName = "John";
let currentDate = new Date();
let userScores = [1, 2, 3];
// 4. Use is/has/can prefixes for boolean values
let isLoggedIn = true;
let hasPermission = false;
let canEdit = true;
// 5. For private variables, can use underscore prefix (convention)
let _privateVar = "private";
let _internalCounter = 0;Understanding Scope
Scope determines the accessibility range of variables, just like different rooms in a house having different access permissions.
Global Scope
Variables declared outside any function or code block have global scope and can be accessed anywhere in the code.
// Global scope
let globalVar = "I am global";
const GLOBAL_CONST = 100;
function test() {
console.log(globalVar); // Can access
console.log(GLOBAL_CONST); // Can access
}
if (true) {
console.log(globalVar); // Can access
}
test();Function Scope
Variables declared inside a function can only be accessed within that function.
function myFunction() {
let localVar = "I am local";
console.log(localVar); // Can access
}
myFunction();
console.log(localVar); // ReferenceError: localVar is not definedBlock Scope
Variables declared with let and const have block scope, only valid within the code block (areas surrounded by {}) where they are declared.
if (true) {
let blockVar = "I am in a block";
const BLOCK_CONST = 200;
console.log(blockVar); // Can access
console.log(BLOCK_CONST); // Can access
}
console.log(blockVar); // ReferenceError
console.log(BLOCK_CONST); // ReferenceError
// Block scope in loops
for (let i = 0; i < 3; i++) {
// i is a new variable each loop
setTimeout(() => console.log(i), 100);
}
// Output: 0, 1, 2
// Compare with var behavior
for (var j = 0; j < 3; j++) {
// All callbacks share the same j
setTimeout(() => console.log(j), 100);
}
// Output: 3, 3, 3Variable Hoisting
Variable hoisting is a JavaScript feature where variable and function declarations are moved to the top of their scope. Understanding this concept is important, although modern code rarely relies on it.
Hoisting of var
console.log(myVar); // undefined
var myVar = "Hello";
// The above code is actually executed as:
var myVar;
console.log(myVar);
myVar = "Hello";Hoisting of let and const
let and const are also hoisted, but they are not initialized, causing a "Temporal Dead Zone."
console.log(myLet); // ReferenceError: Cannot access 'myLet' before initialization
let myLet = "Hello";
// Temporal Dead Zone (TDZ)
let x = 1;
{
// In this block, x is not available before declaration
console.log(x); // ReferenceError
let x = 2; // This x is a new variable, shadowing the outer x
}Which Declaration Method to Choose
This is an important decision in actual development. Here are the recommended selection strategies:
// 1. Use const by default
const userName = "Alex";
const userAge = 30;
const config = { theme: "dark" };
// 2. Only use let when variables need to be reassigned
let counter = 0;
counter++;
counter++;
let currentPage = 1;
currentPage = 2;
// 3. Avoid var (unless need to maintain compatibility with old code)
// Not recommended
var oldStyleVar = "avoid this";
// Practical example: loops
// ✅ Recommended
for (let i = 0; i < 10; i++) {
console.log(i);
}
// ❌ Not recommended
for (var j = 0; j < 10; j++) {
console.log(j);
}
// Practical example: configuration object
// ✅ Recommended
const appConfig = {
name: "MyApp",
version: "1.0.0",
};
// Can modify properties
appConfig.version = "1.0.1";
// But cannot reassign
// appConfig = {}; // TypeErrorDeclaring Without Assignment
Variables can be declared first and assigned values later.
// let can be declared and assigned later
let userName;
console.log(userName); // undefined
userName = "Jessica";
console.log(userName); // "Jessica"
// const must be assigned when declared
const userAge; // SyntaxError: Missing initializer in const declaration
// Correct approach
const userAge = 25;Multiple Variable Declarations
You can declare multiple variables in a single statement.
// Declare separately (recommended, clearer)
let firstName = "Tom";
let lastName = "Smith";
let age = 35;
// Declare together (compact but less readable)
let x = 1,
y = 2,
z = 3;
// Mixed declaration of different types
let name = "Laura",
score = 95,
isPassed = true,
grade; // Can be uninitialized
// const can also do this
const PI = 3.14,
E = 2.718,
GOLDEN_RATIO = 1.618;Common Problems and Solutions
Problem 1: Can const object properties be modified?
Answer: Properties can be modified, but the entire object cannot be reassigned.
const person = {
name: "Anna",
age: 28,
};
// ✅ Can modify properties
person.age = 29;
person.city = "Paris";
// ❌ Cannot reassign
person = { name: "Bob" }; // TypeError
// If you need a completely frozen object
const frozenPerson = Object.freeze({
name: "Charlie",
age: 40,
});
frozenPerson.age = 41; // Silently fails (non-strict mode)
console.log(frozenPerson.age); // Still 40Problem 2: Why recommend let in loops?
Answer: let creates new bindings in each loop iteration, while var shares the same variable.
// Using let (correct)
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i); // Output 0, 1, 2
}, 1000);
}
// Using var (problematic)
for (var j = 0; j < 3; j++) {
setTimeout(() => {
console.log(j); // Output 3, 3, 3
}, 1000);
}
// Solution for var problem (using closures)
for (var k = 0; k < 3; k++) {
(function (index) {
setTimeout(() => {
console.log(index); // Output 0, 1, 2
}, 1000);
})(k);
}Problem 3: What happens with undeclared variables?
Answer: Direct use of undeclared variables creates global variables (non-strict mode) or causes errors (strict mode).
// Non-strict mode
function test() {
undeclaredVar = "Oops!"; // Accidentally creates global variable
}
test();
console.log(undeclaredVar); // "Oops!" - global pollution!
// Strict mode (recommended)
("use strict");
function strictTest() {
undeclaredVar = "Error!"; // ReferenceError: undeclaredVar is not defined
}Summary
Variables and constants are the foundation of JavaScript programming. Choosing the right declaration method makes code safer and more maintainable.
Key Points Review:
- var: Function scope, has variable hoisting, not recommended for use
- let: Block scope, can be reassigned, preferred choice in modern development
- const: Block scope, cannot be reassigned, used for constants and immutable references
- Best practices: Use const by default, use let when changes are needed, avoid var
- Naming rules: Use meaningful names, follow camelCase
- Scope: Understand differences between global, function, and block-level scopes
- Properties of const-declared objects can be modified, but the object reference cannot be changed