Every program you write is, at bottom, a machine for moving values around — a price, a name, a yes-or-no answer. The instant a value matters in more than one place you need a way to refer to it by name, or you end up retyping the same literal everywhere and a single typo quietly breaks the maths.
A variable is a labelled box that holds one value. You give it a name, point that name at a value, and from then on the name stands for the value wherever you use it. Re-point the box at a new value and every later use of the name sees the new one — which is the whole point of having variables, and also the root of about half the bugs you will ever meet.
flowchart LR A["let score"] -->|"= 0"| B["0"] A -->|"score = 10"| C["10"] D["const pi"] --> E["3.14"] D -.->|"reassign? ✗"| F["TypeError"] style C fill:#1e7f3d,color:#fff style E fill:#3776ab,color:#fff style F fill:#b91c1c,color:#fff
Two keywords: let and const
Modern JavaScript gives you two ways to create a binding, and the choice is a real signal, not a coin-flip. Use const when the name should never point at a different value — once you write const pi = 3.14, that name is welded to 3.14 for life. Use let when the value is meant to move, such as a counter you tick upward.
The payoff comes when you read code later. A const whispers 'this name is stable', while a let plants a flag that says 'watch this one, it changes'. Reach for const first and drop to let only when a reassignment is actually part of your plan.
There is a third keyword, var, left over from older JavaScript. It has slippery scope rules that cause real, subtle defects, so treat any var you meet as a warning sign — we will explain precisely why in the functions lesson.
const name = "Ada";
let visits = 3;
visits = visits + 1;
console.log(typeof name, typeof visits, typeof true);
console.log(`${name} has visited ${visits} times`);
Values come in a few shapes
JavaScript keeps its type story deliberately small. The three you will reach for every day are number (covering 42, 3.14, and -7 alike — there is no separate type for integers, which is one less thing to memorise), string (text, written in quotes: "hi"), and boolean (just true and false). Two more round out the foundations: undefined means 'a name that has no value yet', and null is the value you write yourself to signal 'deliberately empty'.
You can ask any value what shape it is with the typeof operator. It returns the type as a string, so typeof 42 is "number" and typeof "hi" is "string". There is one famous wart worth knowing now: typeof null is "object", a historical accident so old it can never be fixed. Remember it and you will not be fooled by it later.
flowchart LR
T["backticks wrap it all"] --> P["${ expression }"]
P -->|"evaluated"| E["age + 1"]
E -->|"spliced into the text"| O["Next year: 31"]
style P fill:#e0900b,color:#fff
style O fill:#1e7f3d,color:#fff
Building strings: template literals
Combining text with values is so common that JavaScript gives it first-class syntax. A template literal is a string wrapped in backticks instead of quotes, and any ${...} inside is treated as live code: the expression is evaluated and dropped straight into the surrounding text.
const age = 30;
console.log(`Next year you'll be ${age + 1}`);
That one line does arithmetic and text in a single breath, and it reads almost like the sentence you would speak aloud. The old alternative is string concatenation with the + operator — "Next year: " + (age + 1) — which works but drowns the meaning in quotes, plus signs, and easy-to-miss brackets. Reach for a template literal whenever a string needs to mention a value, and you will write fewer bugs and far more readable code.
const item = "coffee";
const qty = 2;
const price = 4.5;
const concatenated = "Order: " + qty + " x " + item + " = $" + (qty * price);
const templated = `Order: ${qty} x ${item} = $${qty * price}`;
console.log(concatenated);
console.log(templated);
What you can call a variable
A name must start with a letter, an underscore, or a $, and can then contain digits — so score, _temp, and $price2 are all fine, while 2nd and full-name are not (a dash reads as subtraction). Names are case-sensitive: score and Score are two completely different variables, a distinction that has caused untold suffering in codebases the world over.
You also cannot redeclare a const or let that already exists in the same scope — JavaScript treats that as an error, which is genuinely a feature: it stops two distant parts of your program from unknowingly grabbing the same name. Pick clear, specific names (averageScore will serve you far better than x), and let the name describe the meaning of the value rather than its type.
flowchart TD
Q{Will the value change?} -- No --> C[use const]
Q -- Yes --> L[use let]
style C fill:#e0900b,color:#fff
style L fill:#e0900b,color:#fff
console.log(typeof 42); console.log(typeof 3.14); console.log(typeof "hi"); console.log(typeof true); console.log(typeof undefined); console.log(typeof null);
What does this print? Track the reassignment, and remember typeof reports the type as a string.
let count = 3;
count = count + 1;
const label = "visits";
console.log(typeof count);
console.log(typeof label);
console.log(`${label}: ${count}`);
count started at 3, then had 1 added.
typeof returns a string such as "number" or "string".
The template literal splices the new count into the text.
Write a function celsiusToF(c) that returns the Fahrenheit value using F = C × 9/5 + 32.
function celsiusToF(c) {
// return the Fahrenheit value
}
Multiply by 9, divide by 5, then add 32.
Use the
returnkeyword to send the result back.
function celsiusToF(c) {
return c * 9 / 5 + 32;
}
This counter is meant to be topped up, but the author reached for const — so the very first topUp() throws TypeError: Assignment to constant variable. Change the single keyword that decides whether reassignment is allowed, so topUp() returns 15 and then 20.
const total = 10;
function topUp() {
total = total + 5;
return total;
}
const forbids reassignment; pick the keyword that allows it.
Use let when a value must change over time.
let total = 10;
function topUp() {
total = total + 5;
return total;
}
Write receipt(item, qty, price) that returns a single line built with a template literal, in exactly this shape: 3 x coffee = $9.00. The total must show two decimal places. So receipt("coffee", 3, 3) returns "3 x coffee = $9.00".
function receipt(item, qty, price) {
// build and return the line with a template literal
}
Compute the total, then format it with total.toFixed(2).
In a template literal, write a literal dollar as $ immediately followed by ${...}.
function receipt(item, qty, price) {
const total = qty * price;
return `${qty} x ${item} = $${total.toFixed(2)}`;
}
What does this print? const locked the name, but did it lock the array? Remember console.log prints an array as compact JSON.
const list = [1, 2]; list.push(3); console.log(list); console.log(typeof list);
push changes the array's contents, not which array the name refers to.
An array prints as compact JSON, like [1,2,3].
typeof an array is "object" — there is no "array" type string.
Recap
- A variable is a name pointing at a value; reassigning moves the pointer, not the value.
- Reach for
constby default; useletonly when the value must change. Avoidvar. - Core types are number, string, and boolean, plus
undefinedandnull; ask any value its type withtypeof. - Template literals splice expressions into text with
${...}, far more readable than+concatenation. constlocks the binding, not the contents — arrays and objects behind one can still change.
Next you will package these values into reusable functions, where a private scope keeps each function's names from colliding with the rest of your program.