What a Variable Really Is
A variable is a name that points at a value. When you write const price = 250, JavaScript stores the number 250 in memory and remembers that the name price refers to it. From then on you can write price anywhere you would have written 250, and if the value ever needs to change you change it in exactly one place.
JavaScript is dynamically typed, which means you never declare what kind of value a name will hold. The same variable can hold a number now and a string a moment later. That flexibility is convenient, and it is also the source of a whole family of bugs, because nothing stops you from storing a string where the rest of your code expects a number. Lesson 3 on comparison and Lesson 14 on numbers both come back to what this costs.
Declaring and assigning are two separate steps, even though you usually write them on one line. let total; declares the name and leaves it holding undefined. total = 0; assigns a value. Writing let total = 0; does both at once, and that is what you should normally do — a variable that exists but holds nothing useful is a bug waiting for a chance.
// Declare and assign in one step (the normal case)
const studentName = 'Ananya Sharma';
let marksObtained = 78;
// Declare now, assign later (occasionally useful)
let grade;
console.log(grade); // undefined - the name exists, the value does not
grade = marksObtained >= 75 ? 'Distinction' : 'Pass';
console.log(grade); // 'Distinction'
// Dynamically typed: the same name can hold a different type later
let value = 42; // a number
value = 'forty two'; // now a string - JavaScript does not object const, let and var — and Why var Lost
There are three keywords for declaring variables, and modern JavaScript effectively uses two of them. Reach for const first. Use let only when you genuinely need to point the name somewhere else later. Treat var as something you read in old code and do not write in new code.
The real difference between them is scope — the region of code in which a name is visible. let and const are block-scoped: a name declared inside any pair of curly braces exists only inside those braces. var is function-scoped: it ignores blocks entirely and leaks out to the whole function that contains it. That leak is what makes var dangerous. A counter declared inside an if block with var is still alive after the block finishes and can quietly collide with a name somewhere else.
Preferring const is not pedantry either. When you are reading an unfamiliar function and you see const, you know instantly that the name will never point anywhere else for the rest of that block — one fewer thing to hold in your head while hunting a bug. let then becomes a genuine signal that says "this one really does change", which is useful information instead of noise.
// Block scope: let and const stay inside their braces
if (true) {
let insideOnly = 'block-scoped';
const alsoInside = 'block-scoped';
var leaksOut = 'function-scoped';
}
console.log(leaksOut); // 'function-scoped' - var escaped the block
// console.log(insideOnly); // ReferenceError: insideOnly is not defined
// const cannot be reassigned
const gstRate = 0.18;
// gstRate = 0.28; // TypeError: Assignment to constant variable.
// let can be reassigned
let attempts = 0;
attempts = attempts + 1; // fine - Redeclaring the same name with
varin the same scope is silently allowed, which hides typos beautifully.letandconstthrow aSyntaxErrorinstead, so the mistake surfaces the moment you make it.
Hoisting and the Temporal Dead Zone
Before running your code, JavaScript scans each scope and registers every declaration it finds. That scan is called hoisting, and it explains behaviour that otherwise looks like magic. A var declaration is hoisted and immediately given the value undefined, so you can reference the name on a line above its declaration and get undefined rather than an error. Almost nobody actually wants that, because it turns a typo into a silently wrong answer instead of a crash.
let and const are hoisted too, but they are deliberately left without a value. From the top of the block down to the line that declares them, the name sits in what the specification calls the temporal dead zone, and touching it throws ReferenceError: Cannot access 'b' before initialization. That error is a feature, not an inconvenience — it points at the exact line where you got the order wrong instead of letting undefined drift through the rest of your program.
Function declarations behave differently again: they are hoisted completely, body and all, which is why you can call sayHello() on line one and write function sayHello() on line twenty. A function stored in a const, however, obeys the const rule and is unusable until its line has run. That asymmetry between the two ways of defining a function catches people out constantly, and it is a standard interview question.
console.log(a); // undefined - var hoisted and initialised to undefined
var a = 5;
// console.log(b); // ReferenceError: Cannot access 'b' before initialization
let b = 5;
sayHello(); // works - function declarations hoist completely
function sayHello() {
console.log('Hello');
}
// sayHi(); // ReferenceError - a const follows the dead-zone rule
const sayHi = () => console.log('Hi'); - A question you will meet: what does
console.log(x); var x = 10;print? The answer isundefined— not10, and not an error. Being able to explain why is the thing actually being tested.
const Does Not Mean Unchangeable
This is the most misunderstood rule in the language, so it is worth stating plainly: const protects the binding, not the value. It guarantees the name will keep pointing at the same thing. It says nothing at all about whether that thing can be modified internally.
For numbers, strings and booleans the distinction never shows, because those values cannot be modified at all. For objects and arrays it shows constantly. const cart = [] means cart will always refer to that one array; you can still push items into it, empty it, or sort it in place. Only cart = somethingElse is forbidden.
This is not a flaw, and you should not fight it. Most real code declares arrays and objects with const precisely because reassigning them is nearly always a mistake while changing their contents is the entire point. If you genuinely need a value that cannot be modified, Object.freeze() gives you a shallow guard: it stops properties being added, removed or changed on that object, but any object nested inside it is still wide open.
const cart = ['notebook', 'pen'];
cart.push('calculator'); // allowed - the array itself is modified
console.log(cart); // ['notebook', 'pen', 'calculator']
// cart = ['nothing']; // TypeError - the binding cannot be moved
const student = { name: 'Rahul', marks: 68 };
student.marks = 74; // allowed
console.log(student.marks); // 74
// Object.freeze is a shallow guard, not a deep one
const config = Object.freeze({ retries: 3, nested: { level: 1 } });
config.retries = 5; // ignored (throws in strict mode)
config.nested.level = 2; // still allowed - freeze goes only one level deep
console.log(config.retries, config.nested.level); // 3 2 - Say it once and remember it:
constfreezes the name, not the contents. Half the confusion about arrays and objects in later lessons disappears the moment that sentence sticks.
The Data Types You Will Actually Meet
JavaScript has seven primitive types and one composite type. A primitive holds a single simple value and cannot be modified in place; assigning it to another variable copies the value across. The composite type is object, and arrays, functions and dates are all objects underneath their friendlier names.
Two of the primitives cause more confusion than the other five. null means "deliberately empty" — you set it yourself to record that a value is known to be absent. undefined means "nothing has been put here yet", and it is what JavaScript supplies on its own for a declared-but-unassigned variable, a missing function argument, or a property that does not exist on an object. A useful rule of thumb: undefined is the language's answer, null is yours.
The remaining two primitives are specialist tools. symbol creates guaranteed-unique property keys and is used mostly inside libraries. bigint holds whole numbers larger than a normal number can represent safely, and is written with an n suffix. You can be productive for a long time without writing either, so recognise the names and move on.
- string — text, written in single quotes, double quotes or backticks
- number — every numeric value, whole or decimal; there is no separate integer type
- boolean —
trueorfalse - undefined — declared but not yet given a value
- null — deliberately empty, set by you
- symbol — a unique key, used mainly inside libraries and frameworks
- bigint — integers beyond the safe range of
number, written as9007199254740993n - object — the one non-primitive; arrays, functions and dates are all objects
HTML
<div id="output"></div>
<button onclick="showVariables()">Show Variables</button> CSS
#output { padding: 20px; background: #f0f0f0; border-radius: 5px; margin: 10px 0; min-height: 100px; }
button { padding: 10px 20px; background: #d1039e; color: white; border: none; border-radius: 5px; cursor: pointer; } JavaScript
function showVariables() {
let name = 'Alice';
const age = 30;
let isStudent = false;
let hobbies = ['painting', 'gaming', 'cooking'];
let output = `
Name: ${name}<br>
Age: ${age}<br>
Is Student: ${isStudent}<br>
Hobbies: ${hobbies.join(', ')}<br>
Type of name: ${typeof name}<br>
Type of age: ${typeof age}
`;
document.getElementById('output').innerHTML = output;
} - Notice how
typeofis used in the demo. Printing the type alongside the value is one of the fastest ways to work out why a comparison or a calculation is misbehaving.
Checking Types, and Where typeof Lies
typeof reports the type of a value as a string, and for most primitives it is reliable. It also has two famous defects that you should simply memorise, because they will never be fixed — far too much existing code now depends on them.
First, typeof null returns 'object'. This is a bug from 1995 that became permanent. To test for null, compare directly with value === null. Second, typeof [] also returns 'object', because an array genuinely is an object. To test for an array, use Array.isArray(value), which is precise and reads clearly at the call site.
What remains useful is typeof value === 'string', and the same check against 'number', 'boolean', 'function' and 'undefined'. That last one has a special property worth knowing: typeof is the only operation in the language that will not throw when handed a name that was never declared at all, which makes it the safe way to check whether something exists before using it.
typeof 'Ananya' // 'string'
typeof 78 // 'number'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof function () {} // 'function'
typeof null // 'object' <-- the famous 1995 bug
typeof [] // 'object' <-- an array IS an object
// The checks that are actually correct
const value = null;
console.log(value === null); // true
console.log(Array.isArray([1, 2])); // true
console.log(Array.isArray('abc')); // false typeof NaNis'number'. That looks absurd until you remember what NaN stands for: it is the numeric result of a calculation that failed, so it belongs to the number type. Lesson 14 handles it properly.
Naming Rules and Conventions
The hard rules are short. A name may contain letters, digits, $ and _, and may not begin with a digit. It may not be a reserved word such as class, return or new. Everything beyond that is convention — but convention is what makes your code readable to the next person, and in a code review or an interview it is judged just as closely as the logic.
JavaScript uses camelCase for variables and functions: totalMarks, calculateGst. Classes use PascalCase: StudentRecord. Fixed configuration values are often written in UPPER_SNAKE_CASE: MAX_RETRIES. Following these makes your code look like every other JavaScript codebase in the world, which is exactly the point.
Beyond casing, name things after what they mean rather than what they are. d tells the reader nothing; daysUntilExam tells them everything and costs three extra seconds to type. Booleans read best with an is, has or can prefix — isSubmitted, hasPaid — because the if statement that uses them then reads as an English sentence.
- Allowed in a name: letters, digits,
$and_— but never a digit first - Not allowed: reserved words such as
let,class,return,new - Variables and functions:
camelCase - Classes and constructor functions:
PascalCase - Fixed configuration values:
UPPER_SNAKE_CASE - Booleans: prefix with
is,hasorcan - Case matters —
totalMarksandtotalmarksare two different variables
