JavaScript Variables Complete Master Guide
JavaScript Variables: Complete Overview
JavaScript provides four main ways to declare variables: var, let, const, and global variables. Each has distinct characteristics, scoping rules, and use cases. Understanding how they relate to each other and when to use each type is fundamental to writing effective JavaScript code.
The Four Variable Types in JavaScript
var- Function-scoped, hoisted, can be re-declared - the original variable declarationlet- Block-scoped, cannot be re-declared, allows reassignment - modern replacement for varconst- Block-scoped, cannot be re-declared or reassigned - use by default for constantsGlobal Variables- Declared without any keyword or attached to global object - accessible everywhere
var - The Original Variable Declaration
var was the original way to declare variables in JavaScript. It's function-scoped, hoisted, and allows re-declaration. While largely replaced by let and const in modern code, understanding var is crucial for working with legacy codebases.
1// Example 1: Basic var usage
2var firstName = "John";
3var age = 30;
4var isActive = true;
5
6// Example 2: Function scoping
7function varExample() {
8 if (true) {
9 var functionScoped = "I'm accessible everywhere in this function";
10 }
11 console.log(functionScoped); // Works - no block scope
12}
13
14// Example 3: Hoisting behavior
15console.log(hoistedVar); // undefined (not error)
16var hoistedVar = "I was hoisted";
17
18// Example 4: Re-declaration allowed
19var counter = 5;
20var counter = 10; // No error - re-declaration permitted
21
22// Example 5: No temporal dead zone
23function hoistingTest() {
24 console.log(x); // undefined
25 var x = 10;
26}
27
28// Example 6: Loop issues with closures
29for (var i = 0; i < 3; i++) {
30 setTimeout(() => console.log(i), 100); // Logs: 3, 3, 3
31}
32
33// Example 7: Comparison with modern alternatives
34var oldWay = "function scoped";
35let modernWay = "block scoped";
36const constantWay = "immutable";var Key Characteristics
Scope- Function-scoped - accessible throughout entire functionHoisting- Fully hoisted - can be accessed before declaration (value: undefined)Re-declaration- Allowed - can declare same variable multiple timesUse Case- Legacy code maintenance only - avoid in new code
let - Modern Block-Scoped Variables
let was introduced in ES6 as a modern replacement for var. It provides block scoping, prevents re-declaration, and has more predictable behavior. Use let when you need to reassign variables within block scope.
1// Example 1: Basic let usage
2let userName = "Alice";
3let score = 95.5;
4let isOnline = false;
5
6// Example 2: Block scoping
7function letExample() {
8 if (true) {
9 let blockScoped = "I'm only accessible in this block";
10 console.log(blockScoped); // Works
11 }
12 // console.log(blockScoped); // Error - not accessible
13}
14
15// Example 3: No hoisting initialization
16// console.log(letVariable); // ReferenceError: Cannot access before initialization
17let letVariable = "I'm not hoisted like var";
18
19// Example 4: No re-declaration
20let uniqueVar = "first declaration";
21// let uniqueVar = "second declaration"; // SyntaxError: Already declared
22
23// Example 5: Reassignment allowed
24let counter = 0;
25counter = 1; // Valid
26counter++; // Valid
27counter += 5; // Valid
28
29// Example 6: Proper loop behavior
30for (let i = 0; i < 3; i++) {
31 setTimeout(() => console.log(i), 100); // Logs: 0, 1, 2
32}
33
34// Example 7: Temporal Dead Zone
35{
36 // console.log(tdzLet); // ReferenceError: TDZ
37 let tdzLet = "now initialized";
38 console.log(tdzLet); // Works
39}let Key Characteristics
Scope- Block-scoped - accessible only within declaring blockHoisting- Hoisted but not initialized - temporal dead zone existsRe-declaration- Not allowed - cannot declare same variable in same scopeUse Case- When variable needs reassignment within block scope
const - Immutable Block-Scoped Constants
const provides block-scoped variables that cannot be reassigned. It requires initialization at declaration and is the preferred choice for variables that shouldn't change. Note: const doesn't make objects/arrays immutable, only prevents reassignment.
1// Example 1: Basic const usage
2const PI = 3.14159;
3const API_KEY = "abc123def456";
4const MAX_USERS = 1000;
5
6// Example 2: Must be initialized
7// const UNINITIALIZED; // SyntaxError: Missing initializer
8const INITIALIZED = "I have a value";
9
10// Example 3: No reassignment
11const TAX_RATE = 0.07;
12// TAX_RATE = 0.08; // TypeError: Assignment to constant variable
13// TAX_RATE++; // Error - would require reassignment
14
15// Example 4: Objects/arrays can be modified
16const user = { name: "John", age: 30 };
17user.age = 31; // ✅ Allowed - modifying property
18user.city = "Boston"; // ✅ Allowed - adding property
19
20const colors = ["red", "green"];
21colors.push("blue"); // ✅ Allowed - modifying array
22colors[0] = "orange"; // ✅ Allowed - changing element
23
24// Example 5: Block scoping
25{
26 const BLOCK_CONST = "only in this block";
27 console.log(BLOCK_CONST); // Works
28}
29// console.log(BLOCK_CONST); // Error - not accessible
30
31// Example 6: const in loops
32const numbers = [1, 2, 3];
33for (const num of numbers) {
34 console.log(num); // 1, 2, 3 - new binding each iteration
35}
36
37// Example 7: Function expressions
38const calculateArea = (radius) => {
39 const PI = 3.14159;
40 return PI * radius * radius;
41};Global Variables - Implicit Declarations
Global variables are accessible from anywhere in your code. They can be created by declaring variables without any keyword, attaching to the global object, or declaring outside any function. Global variables should be minimized as they can cause naming conflicts and hard-to-track bugs.
1// Example 1: Implicit global (avoid this)
2function createGlobal() {
3 accidentalGlobal = "I'm a global variable"; // No declaration keyword
4}
5createGlobal();
6console.log(accidentalGlobal); // "I'm a global variable" - pollutes global scope
7
8// Example 2: Explicit global
9var explicitGlobal = "I'm a global var"; // Declared outside any function
10let modernGlobal = "I'm a global let"; // Also global when declared outside functions
11const GLOBAL_CONST = "I'm a global const"; // Global constant
12
13// Example 3: Attaching to global object
14window.browserGlobal = "I'm on window object"; // Browser
15global.nodeGlobal = "I'm on global object"; // Node.js
16
17// Example 4: Global scope pollution
18var commonName = "original";
19
20function library1() {
21 commonName = "changed by library1"; // Modifies global
22}
23
24function library2() {
25 var commonName = "library2 version"; // Creates local (shadows global)
26}
27
28// Example 5: Strict mode prevents implicit globals
29function strictExample() {
30 "use strict";
31 // undeclaredVar = "error"; // ReferenceError: variable is not defined
32 var properVar = "correct";
33}
34
35// Example 6: Namespacing pattern to avoid pollution
36var MY_APP = MY_APP || {}; // Create namespace
37MY_APP.config = { apiUrl: "https://api.example.com" };
38MY_APP.utils = { calculate: function() {} };
39
40// Example 7: IIFE to avoid globals
41(function() {
42 var privateVar = "I'm not global";
43 // Code here doesn't pollute global scope
44})();Complete Variable Comparison Table
Understanding the differences between all variable types helps you choose the right tool for each situation. This comprehensive comparison highlights key characteristics and use cases.
1// Side-by-side comparison in code
2
3// SCOPE COMPARISON
4function scopeComparison() {
5 if (true) {
6 var functionScoped = "var: function scope";
7 let blockScopedLet = "let: block scope";
8 const blockScopedConst = "const: block scope";
9 implicitGlobal = "global: becomes global";
10 }
11
12 console.log(functionScoped); // ✅ Works - function scoped
13 // console.log(blockScopedLet); // ❌ Error - block scoped
14 // console.log(blockScopedConst); // ❌ Error - block scoped
15 console.log(implicitGlobal); // ✅ Works - global
16}
17
18// HOISTING COMPARISON
19console.log(varHoisted); // ✅ undefined (hoisted)
20// console.log(letHoisted); // ❌ ReferenceError (TDZ)
21// console.log(constHoisted); // ❌ ReferenceError (TDZ)
22// console.log(globalHoisted); // ❌ ReferenceError
23
24var varHoisted = "hoisted";
25let letHoisted = "not hoisted";
26const constHoisted = "not hoisted";
27globalHoisted = "not hoisted";
28
29// RE-DECLARATION COMPARISON
30var canRedeclare = "first";
31var canRedeclare = "second"; // ✅ Allowed
32
33let noRedeclare = "first";
34// let noRedeclare = "second"; // ❌ SyntaxError
35
36const noRedeclareConst = "first";
37// const noRedeclareConst = "second"; // ❌ SyntaxError
38
39// Can shadow globals with same name
40var globalVar = "global";
41function test() {
42 var globalVar = "local"; // ✅ Shadows global
43 console.log(globalVar); // "local"
44}Variable Types Comparison Matrix
Scope- var: Function | let: Block | const: Block | Global: GlobalHoisting- var: Yes (undefined) | let: Yes (TDZ) | const: Yes (TDZ) | Global: NoRe-declaration- var: Allowed | let: Not allowed | const: Not allowed | Global: AllowedReassignment- var: Allowed | let: Allowed | const: Not allowed | Global: AllowedInitialization- var: Optional | let: Optional | const: Required | Global: RequiredUse Case- var: Legacy code | let: Reassignable | const: Constants | Global: Avoid
How Variables Relate and Connect
Understanding the relationships between different variable types helps you see how JavaScript has evolved and how to migrate between different declaration styles effectively.
1// RELATIONSHIP 1: Evolution from var to let/const
2// Old way with var (ES5 and earlier)
3var oldVariables = "function scoped";
4for (var i = 0; i < 3; i++) {
5 // i is shared across all iterations
6}
7
8// Modern way with let/const (ES6+)
9let modernVariables = "block scoped";
10for (let i = 0; i < 3; i++) {
11 // i is unique to each iteration
12}
13
14// RELATIONSHIP 2: Scope chain connections
15const globalConstant = "I'm global";
16
17function outerFunction() {
18 var outerVar = "I'm in outer function";
19
20 function innerFunction() {
21 let innerLet = "I'm in inner function";
22
23 // All outer variables are accessible
24 console.log(globalConstant); // ✅ Accessible
25 console.log(outerVar); // ✅ Accessible (closure)
26 console.log(innerLet); // ✅ Accessible
27 }
28
29 innerFunction();
30}
31
32// RELATIONSHIP 3: Migration path from var to const/let
33// Before: var for everything
34var counter = 0;
35var user = { name: "John" };
36var isActive = true;
37
38// After: Appropriate declarations
39let counter = 0; // let - will change
40const user = { name: "John" }; // const - object reference won't change
41const isActive = true; // const - boolean won't change
42
43// RELATIONSHIP 4: Global pollution prevention
44// Problem: Implicit globals
45function problematic() {
46 globalPolluter = "I pollute global scope";
47}
48
49// Solution: Explicit declarations
50function clean() {
51 const localConstant = "I stay local";
52 let localVariable = "I also stay local";
53 var localOldStyle = "I'm function scoped";
54}
55
56// RELATIONSHIP 5: Temporal Dead Zone awareness
57{
58 // Temporal Dead Zone for let/const
59 // console.log(tdzLet); // ❌ ReferenceError
60 // console.log(tdzConst); // ❌ ReferenceError
61
62 console.log(tdzVar); // ✅ undefined (var is hoisted)
63
64 let tdzLet = "now safe";
65 const tdzConst = "now safe";
66 var tdzVar = "hoisted";
67}Migration Patterns and Best Practices
Learn how to migrate from older patterns to modern JavaScript variable declarations, and understand the best practices for using each variable type in contemporary code.
1// MIGRATION PATTERN 1: var → const/let
2// Legacy code with var
3var app = app || {};
4var config = {
5 apiUrl: "https://api.example.com",
6 timeout: 5000
7};
8var currentUser = null;
9
10// Modern migration
11const APP_NAMESPACE = window.APP_NAMESPACE || {};
12const CONFIG = Object.freeze({
13 API_URL: "https://api.example.com",
14 TIMEOUT: 5000
15});
16let currentUser = null; // Will be reassigned
17
18// MIGRATION PATTERN 2: Fixing loop issues
19// Problem: var in loops with closures
20for (var i = 0; i < 3; i++) {
21 setTimeout(() => console.log(i), 100); // 3, 3, 3
22}
23
24// Solution 1: Use let
25for (let i = 0; i < 3; i++) {
26 setTimeout(() => console.log(i), 100); // 0, 1, 2
27}
28
29// Solution 2: IIFE with var (legacy fix)
30for (var i = 0; i < 3; i++) {
31 (function(j) {
32 setTimeout(() => console.log(j), 100); // 0, 1, 2
33 })(i);
34}
35
36// MIGRATION PATTERN 3: Global variable containment
37// Before: Polluting global scope
38var globalData = {};
39function init() {
40 globalData.users = [];
41}
42
43// After: Module pattern
44const App = (function() {
45 const privateData = {};
46
47 return {
48 init: function() {
49 privateData.users = [];
50 },
51 getUsers: function() {
52 return privateData.users;
53 }
54 };
55})();
56
57// BEST PRACTICE 1: Declaration preferences
58// ✅ Preferred order: const → let → avoid var → avoid globals
59const API_BASE = "https://api.example.com"; // Use const by default
60let isLoading = false; // Use let for reassignables
61// var oldVariable = "avoid"; // Avoid var in new code
62// implicitGlobal = "never"; // Never use implicit globals
63
64// BEST PRACTICE 2: Naming conventions
65const CONSTANT_VALUES = "UPPER_SNAKE_CASE";
66let regularVariables = "camelCase";
67var legacyVars = "camelCase";
68
69// BEST PRACTICE 3: Scope minimization
70function goodPractice() {
71 const MAX_RETRIES = 3; // Declare close to usage
72
73 for (let i = 0; i < MAX_RETRIES; i++) {
74 const attemptMessage = `Attempt ${i + 1}`;
75 console.log(attemptMessage);
76 }
77}