JavaScript tutorials  /  JavaScript Variables and Data Types
Chapter 2 · JavaScript

JavaScript Variables and Data Types

A variable in JavaScript is a named container for storing a value, declared using one of three keywords: 'var' (the original, function-scoped declaration), 'let' (block-scoped, reassignable), or 'const' (block-scoped, cannot be reassigned after initialization). JavaScript is dynamically typed, meaning a variable's data type is determined automatically at runtime based on its current value, and can change if reassigned.

Think of 'let', 'const', and 'var' as three different types of labeled boxes you can create. A 'const' box is sealed shut after you first put something in it — you can look inside, but you can never swap out its contents for something else entirely (though if it holds an object, you can still change details INSIDE that object). A 'let' box can be opened and have its contents completely swapped out anytime. A 'var' box is like an older-style box whose visibility rules (where in your house you're allowed to see it) work a bit more loosely and unpredictably compared to the more modern 'let' and 'const' boxes.

Consider a shopping cart feature on an e-commerce website's frontend JavaScript code. A 'const TAX_RATE = 0.08;' would be used for a fixed tax rate that should never accidentally change during the checkout process. A 'let cartTotal = 0;' would be used for the running total, since its value needs to be updated (reassigned) every time an item is added or removed from the cart. Using 'const' for values that shouldn't change and 'let' for values that will change is a real, widely-followed convention across professional JavaScript codebases, helping prevent accidental bugs where a value that should remain constant gets unintentionally overwritten somewhere else in a large codebase.

Variables allow a program to store and manipulate data as it runs, and JavaScript's introduction of 'let' and 'const' (in ES6/2015) specifically addressed confusing and bug-prone behaviors inherent in the original 'var' keyword, particularly around scoping (where a variable is actually accessible) and accidental reassignment. Understanding JavaScript's dynamic typing is also essential, since the language automatically converts between types in many situations (a behavior called type coercion), which can be either a convenient feature or a significant source of bugs if not properly understood.

  • Primitive Data Types: Immutable, basic data types: 'Number' (represents both integers and decimals, unlike Java's separate int/double), 'String', 'Boolean', 'undefined' (a variable declared but not yet assigned a value), 'null' (an intentional absence of any value), 'Symbol', and 'BigInt' (for arbitrarily large integers).
  • Reference (Object) Data Types: Types that store a reference to a location in memory rather than the value directly, including plain 'Object', 'Array', 'Function', and 'Date', all of which are technically specialized kinds of objects in JavaScript.
  • var (Function-Scoped Declaration): The original variable declaration keyword, scoped to the nearest enclosing FUNCTION (not block), and subject to 'hoisting', where the declaration is conceptually moved to the top of its scope, initialized as 'undefined'.
  • let and const (Block-Scoped Declarations): Modern (ES6+) variable declarations scoped to the nearest enclosing BLOCK (like an if statement or loop), with 'let' allowing reassignment and 'const' preventing it after initial assignment; both avoid var's confusing hoisting-related pitfalls.
let variableName = value; const constantName = value; var oldStyleVariable = value; // Examples: let age = 25; const PI = 3.14159; let name = "CompileX";
A beginner needs to store a student's roll number, height, name, and pass/fail status using appropriate variable declarations, understanding when to use 'let' versus 'const', and wants to see how JavaScript's dynamic typing and the 'typeof' operator work in practice.
Declaring Variables with let and const, and Checking Types
This example creates variables using both 'let' and 'const' to store a student's details, then uses the 'typeof' operator to inspect each variable's actual data type at runtime.
JavaScript
const rollNumber = 101; let height = 5.8; let name = "Aditi"; let hasPassed = true; console.log("Roll Number:", rollNumber, "| Type:", typeof rollNumber); console.log("Height:", height, "| Type:", typeof height); console.log("Name:", name, "| Type:", typeof name); console.log("Passed:", hasPassed, "| Type:", typeof hasPassed);
Roll Number: 101 | Type: number Height: 5.8 | Type: number Name: Aditi | Type: string Passed: true | Type: boolean
'rollNumber' is declared with 'const' since a student's roll number shouldn't ever be reassigned once set, while 'height', 'name', and 'hasPassed' use 'let' since they represent values that could reasonably change. Notice both 'rollNumber' (101) and 'height' (5.8) report as the same 'number' type — unlike Java, JavaScript does NOT distinguish between integers and decimals as separate types; there is only one unified 'Number' type for all numeric values. The 'typeof' operator returns a string identifying each variable's current data type.
const Prevents Reassignment, But Not Mutation of Object Contents
This example demonstrates that 'const' prevents a variable from being reassigned to a completely new value, but does NOT prevent modifying the internal properties of an object or array that the const variable references.
JavaScript
const student = { name: "Bob", grade: "B" }; student.grade = "A"; // Allowed: modifying a property console.log(student); try { student = { name: "Charlie", grade: "C" }; // Not allowed: reassigning const } catch (error) { console.log("Error:", error.message); }
{ name: 'Bob', grade: 'A' } Error: Assignment to constant variable.
'student.grade = "A";' is allowed because it modifies a PROPERTY of the existing object that 'student' points to — the variable 'student' itself never gets reassigned to a different object. However, attempting 'student = { ... }' tries to make the 'student' variable point to an ENTIRELY NEW object, which directly violates 'const's core rule of never being reassignable after initialization, causing a TypeError. This demonstrates that 'const' only makes the variable BINDING immutable, not necessarily the object's own internal content.
  • Using var and Encountering Unexpected Function-Scoping Behavior: Since 'var' is function-scoped (not block-scoped), declaring 'var x = 10;' inside an 'if' block makes 'x' accessible even OUTSIDE that if block, as long as it's still within the same enclosing function — this often surprises beginners expecting 'var' to behave like 'let', which is properly confined to the block it was declared in.
  • Attempting to Reassign a const Variable: Writing 'const total = 100;' and later trying 'total = 200;' results in a runtime 'TypeError: Assignment to constant variable.' Beginners sometimes use 'const' out of habit for values they later realize need to change, requiring them to go back and switch the declaration to 'let'.
  • Confusing 'undefined' with 'null': 'undefined' means a variable has been declared but has not yet been assigned any value at all (JavaScript's own automatic default), while 'null' represents an INTENTIONAL, deliberate absence of a value that a developer explicitly assigned themselves. Beginners often use these interchangeably, not realizing 'typeof undefined' returns "undefined" while, confusingly, 'typeof null' returns "object" (a long-standing, well-known quirk/bug in JavaScript's design that has never been fixed for backward-compatibility reasons).
  • Relying on Implicit Type Coercion Without Understanding It: Writing '"5" + 3' produces the STRING "53" (concatenation, since one operand is already a string), while '"5" - 3' produces the NUMBER 2 (since '-' has no string-concatenation meaning, so JavaScript automatically converts "5" to a number first). Beginners unfamiliar with these automatic type coercion rules are often confused by seemingly inconsistent behavior across different operators when mixing strings and numbers.
  • Default to const, Use let Only When Reassignment Is Needed: Declare variables with 'const' by default, and only switch to 'let' when you know the variable's value will genuinely need to be reassigned later in the code. This makes your intent clear to other developers and helps prevent accidental reassignment bugs, since attempting to reassign a 'const' immediately raises a clear error rather than silently succeeding.
  • Avoid Using var in Modern JavaScript Code: Prefer 'let' and 'const' exclusively over 'var' in all new code, since their block-scoping behavior is far more predictable and aligns with how variable scoping works in most other modern programming languages, avoiding var's confusing function-scoping and hoisting-related pitfalls entirely.
  • Use === and !== Instead of == and != for Comparisons: Always use the strict equality operator '===' (and '!==') rather than the loose equality operator '==' (and '!='), since '==' performs implicit type coercion before comparing, which can produce surprising, unintuitive results (e.g., '0 == false' is true), while '===' compares both value AND type, without any automatic conversion, leading to far more predictable and bug-resistant comparisons.
  • Use typeof or Explicit Checks to Verify Types When Debugging: When unsure of a variable's current type (especially important given JavaScript's dynamic typing and automatic coercion), use the 'typeof' operator for a quick check, and be especially mindful of its known quirk where 'typeof null' returns "object" rather than "null".
What are the key differences between var, let, and const in JavaScript?
'var' is function-scoped, meaning it's accessible anywhere within the entire enclosing function (even outside a block like an if-statement or loop where it was declared), and it is 'hoisted' with its declaration effectively moved to the top of its scope, initialized as 'undefined' until the actual assignment line runs. 'let' is block-scoped, meaning it's only accessible within the nearest enclosing curly-brace block (like an if statement, loop, or function body), and while it is technically also hoisted, accessing it before its declaration line throws a 'ReferenceError' due to what's called the 'temporal dead zone', rather than silently returning undefined like var. 'const' behaves identically to 'let' in terms of block-scoping, but additionally prevents the variable from ever being reassigned to a new value after its initial assignment — though, importantly, if a const variable holds an object or array, the object's own internal properties or array elements can still be freely modified, since only the variable's binding to that specific object is protected, not the object's contents.
What is the difference between 'undefined' and 'null' in JavaScript, and why does 'typeof null' return "object"?
'undefined' is JavaScript's own automatic default value, assigned to a variable that has been declared but not yet given any explicit value, or returned by a function that doesn't explicitly return anything. 'null' is a value that a developer must explicitly and intentionally assign themselves, specifically to represent 'no value' or 'empty' in a deliberate way (e.g., 'let selectedUser = null;' to indicate no user is currently selected). The fact that 'typeof null' returns "object" is a widely-acknowledged bug/quirk present since the very first version of JavaScript in 1995 — due to how values were originally represented internally at a low level, 'null' was mistakenly categorized as an object type, and this behavior has been permanently kept for backward compatibility, since fixing it now could break a significant amount of existing code across the web that might inadvertently depend on this specific (technically incorrect) behavior.
Explain JavaScript's implicit type coercion with the '+' and '-' operators, using an example, and explain why they behave differently when mixing strings and numbers.
JavaScript's '+' operator serves dual purposes: numeric addition AND string concatenation. When at least one operand of '+' is a string, JavaScript converts the OTHER operand to a string as well and performs concatenation — so '"5" + 3' produces the string "53", not the number 8. The '-' operator, however, has no meaningful string-related purpose at all (there's no concept of 'string subtraction'), so when used with a string operand, JavaScript instead attempts to convert that string to a NUMBER first, then performs actual numeric subtraction — so '"5" - 3' produces the number 2, not a string. This asymmetry between '+' (which prioritizes string concatenation if any operand is a string) and other arithmetic operators like '-', '*', '/' (which always attempt numeric conversion) is a frequent, well-known source of confusing bugs for developers unfamiliar with JavaScript's specific type coercion rules.
Declare four variables using appropriate 'let' or 'const' choices to store a movie's title (text), rating (decimal number), release year (whole number), and whether it's currently streaming (True/False). Print all four values along with their types using typeof.
const title = "Inception"; let rating = 8.8; const releaseYear = 2010; let isStreaming = true; console.log("Title:", title, typeof title); console.log("Rating:", rating, typeof rating); console.log("Release Year:", releaseYear, typeof releaseYear); console.log("Is Streaming:", isStreaming, typeof isStreaming);
What will be the output of the following code, and explain why, based on var's function-scoping behavior? function testScope() { if (true) { var message = "Hello from inside the if block"; } console.log(message); } testScope();
The output will be: 'Hello from inside the if block'. This happens because 'var' is FUNCTION-scoped, not block-scoped — even though 'message' is declared inside the 'if' block, 'var' makes it accessible anywhere within the entire enclosing 'testScope' function, including the console.log() line that comes after the if block has already finished. If 'let' had been used instead of 'var', this same code would throw a 'ReferenceError: message is not defined', since 'let' is properly confined (block-scoped) only to the if block where it was declared.
Write a JavaScript program that demonstrates the difference between '+' with a string and a number, versus '-' with a string and a number, printing both results and their types.
let result1 = "10" + 5; let result2 = "10" - 5; console.log("Result1:", result1, "| Type:", typeof result1); console.log("Result2:", result2, "| Type:", typeof result2); // Output: // Result1: 105 | Type: string // Result2: 5 | Type: number // The '+' operator concatenates "10" and 5 into the string "105" since one operand is a string. The '-' operator instead converts "10" into the number 10 first (since subtraction has no string meaning), then correctly performs numeric subtraction, producing the number 5.

JavaScript variables are declared using 'var' (older, function-scoped), 'let' (modern, block-scoped, reassignable), or 'const' (modern, block-scoped, not reassignable), with 'let' and 'const' being strongly preferred in current code for their more predictable scoping behavior. JavaScript is dynamically typed with primitive types (Number, String, Boolean, undefined, null, Symbol, BigInt) and reference types (Object, Array, Function), and understanding its automatic type coercion rules — along with the well-known quirks like 'typeof null' returning "object" — is essential for writing predictable, bug-free JavaScript code.

© 2026 CompileX. Maintained by Aditya Kumar Sharma.