JavaScript specials
You’ve now got a working toolkit: variables, types, operators, branches, loops, and functions. This chapter walks back over the whole set, but not as a flat list. The goal is to lock in the subtle moments — the places where JavaScript does something you wouldn’t guess — and to give you a single page you can skim before moving on.
Each section ends with a link to the full lesson, so treat this as a map. If a topic feels shaky, follow the link.
Code structure
Statements are separated by a semicolon:
alert('Hello'); alert('World');
A line break usually counts as a separator too, so this behaves the same way:
alert('Hello')
alert('World')
That convenience is called automatic semicolon insertion (ASI): the engine looks at the line break and, in most cases, acts as if you’d typed a semicolon. The trouble is that word most. ASI has rules, and when a line ends in a way that could continue onto the next line, the engine keeps reading instead of inserting a semicolon.
Here’s the classic trap:
alert("There will be an error after this message")
[1, 2].forEach(alert)
You wrote two statements. The engine sees one. Because a value followed by [...] is valid syntax — that’s property access — it glues the lines together into alert("...")[1, 2].forEach(alert) and then blows up at runtime.
The fix is dull and reliable: end every statement with a semicolon. Most style guides insist on it precisely because it removes this whole category of surprise.
Semicolons are not needed after code blocks {...} or the constructs built on them, like loops and function declarations:
function f() {
// no semicolon needed after a function declaration
}
for (;;) {
// no semicolon needed after the loop
}
And if you do add an extra semicolon where none is required, nothing breaks — the engine treats it as an empty statement and ignores it.
More in: Code structure.
Strict mode
To switch on all of modern JavaScript, start the script with the directive "use strict":
'use strict';
// ...modern behavior from here on
It has to sit at the very top of the script, or at the very top of a function body — nothing but comments may come before it. Put it lower down and it silently does nothing.
Leave it out and your code still runs, but a handful of features fall back to their old, backward-compatible behavior, some of which papers over mistakes you’d rather see. Strict mode is the setting you want.
One convenience: several modern constructs turn strict mode on for you. Code inside a class, and the body of any JavaScript module, is strict automatically — no directive required.
More in: The modern mode, “use strict”.
Variables
You can declare variables with:
let— a normal variable you can reassign.const— a constant; the binding can’t be reassigned after it’s set.var— the old-school declaration, with its own quirks we’ll meet later.
Naming rules for any of them:
- Use letters, digits, and the symbols
$and_. The$and_count as ordinary name characters, on par with letters. - The first character can’t be a digit.
- Non-Latin letters are technically allowed, but real codebases stick to English names.
Variables are dynamically typed — one variable can hold any value, and you can swap the value’s type freely:
let x = 5;
x = "Maya"; // was a number, now a string — perfectly legal
There are 8 data types in the language:
number— integers and floating-point numbers alike.bigint— integers of arbitrary size, past the safe range ofnumber.string— text.boolean—trueorfalse.null— a type with the single valuenull, meaning “empty” or “nothing here”.undefined— a type with the single valueundefined, meaning “not assigned yet”.object— for structured collections of data.symbol— for unique identifiers.
The typeof operator reports a value’s type as a string. It’s honest almost everywhere, with two famous exceptions worth memorizing:
typeof null == "object" // "object" — a long-standing bug in the language, kept for compatibility
typeof function(){} == "function" // functions report "function", not "object"
The first is a mistake baked into JavaScript since the beginning; null is not an object, but typeof says so anyway. The second is a deliberate convenience — functions are technically objects, but typeof singles them out so you can test for callability.
More in: Variables and Data types.
Interaction
We’re learning in the browser, so the three simplest ways to talk to a user are built in:
prompt(question, [default])Ask a
questionwith a text field. Returns whatever the visitor typed, ornullif they hit Cancel.
confirm(question)Ask a
questionwith OK and Cancel buttons. Returnstruefor OK,falsefor Cancel.
alert(message)Show a
messageand wait for the visitor to dismiss it.
All three are modal: they freeze the script and block the page until the visitor answers. Nothing else runs, and the user can’t touch the page, until the dialog closes.
let userName = prompt("Your name?", "Nadia");
let isTeaWanted = confirm("Do you want some tea?");
alert( "Visitor: " + userName ); // Nadia
alert( "Tea wanted: " + isTeaWanted ); // true
One detail that trips people up: prompt always hands back a string, even when the visitor types digits. prompt("Age?") for someone typing 30 gives you "30", not 30. Convert before doing math.
More in: Interaction: alert, prompt, confirm.
Operators
JavaScript’s operators, grouped by what they do:
- Arithmetic
The usual
+ - * /, plus%for the remainder and**for exponentiation.The binary
+does double duty. With numbers it adds; with a string on either side it concatenates, converting the other operand to a string first:alert( '1' + 2 ); // '12' — string, because '1' is a string alert( 1 + '2' ); // '12' — same reasonThat asymmetry is the source of countless bugs, so keep it in mind whenever a
+meets a string.
- Assignments
Plain assignment
a = b, and the combined forms likea *= 2(short fora = a * 2). Assignment is itself an expression that returns the assigned value, which is whya = b = cworks.
- Bitwise
Bitwise operators treat numbers as 32-bit integers and work bit by bit. You’ll rarely need them; reach for the reference when you do.
- Conditional (ternary)
The one operator that takes three parts:
cond ? resultA : resultB. Ifcondis truthy the whole thing evaluates toresultA, otherwiseresultB. It’s an expression, so it produces a value you can assign or return.
- Logical
&&(AND) and||(OR) short-circuit: they stop as soon as the result is decided, and they return the operand where they stopped — not necessarily a boolean.!(NOT) converts its operand to boolean and flips it.alert( 1 && 5 ); // 5 — both truthy, returns the last alert( null || "fallback" ); // "fallback" — first is falsy, returns the second
- Nullish coalescing
a ?? bpicks the first defined value: it returnsaunlessaisnullorundefined, in which case it returnsb. Unlike||, it doesn’t treat0or""as a reason to fall through — onlynull/undefineddo that.
- Comparisons
Loose equality
==converts operands of different types to numbers before comparing, which produces some famous surprises:alert( 0 == false ); // true — false becomes 0 alert( 0 == '' ); // true — '' becomes 0Other comparisons (
<,>,<=,>=) also convert to numbers, with one exception: when both sides are strings, they compare character by character in dictionary order.Strict equality
===skips all conversion. Different types are simply not equal, full stop — which is why it’s the default you should reach for.nullandundefinedare a special pair: with==they equal each other and nothing else. Don’t rely on them converting like other values.alert( null == undefined ); // true alert( null == 0 ); // false — no conversion here
- Others
A few more exist, such as the comma operator, which evaluates several expressions and returns the last one.
More in: Basic operators, maths, Comparisons, Logical operators, Nullish coalescing operator ‘??’.
Loops
We covered three loop shapes. They all repeat a block, but they check the condition at different moments:
// 1 — check first, then maybe run
while (condition) {
// ...
}
// 2 — run once, then check
do {
// ...
} while (condition);
// 3 — counter loop: init, check, step
for (let i = 0; i < 10; i++) {
// ...
}
A few things worth remembering:
- A variable declared in the
for (let ...)header lives only inside the loop. You can drop theletand reuse an outer variable instead, though the scoped version is cleaner. breakbails out of the whole loop;continueskips to the next iteration.- To break out of nested loops in one jump, label the outer loop and write
break labelName.
Details in: Loops: while and for.
Later you’ll meet loops built specifically for walking through objects and other collections.
The “switch” construct
switch replaces a stack of if checks against the same value. It compares each case with the switch value using === — strict equality, no type conversion.
That strictness is exactly where the classic mistake lives:
let age = prompt('Your age?', 18);
switch (age) {
case 18:
alert("Won't work"); // prompt returns a string "18", not the number 18
break;
case "18":
alert("This works!"); // string matches string
break;
default:
alert("Any value not equal to one above");
}
Because prompt gives back the string "18", and switch uses ===, the numeric case 18 never matches. Only case "18" does. When your switch value comes from user input, either convert it up front or write string cases.
Don’t forget break at the end of each case. Leave it off and execution falls through into the next case — occasionally useful, usually a bug.
Details in: The “switch” statement.
Functions
We’ve written functions three ways. They differ in syntax and in a couple of behaviors, but each one packages code you can call by name.
Declaration function sum(a, b) { … }
Expression let sum = function(a, b) { … };
Arrow let sum = (a, b) => a + b;
1. Function Declaration — stands on its own in the main flow of code:
function sum(a, b) {
let result = a + b;
return result;
}
2. Function Expression — created as part of a larger statement, here assigned to a variable:
let sum = function(a, b) {
let result = a + b;
return result;
};
The practical difference: a declaration is hoisted, so you can call it above where it’s written. An expression only exists once execution reaches that line.
3. Arrow functions — a compact expression syntax:
// single expression on the right — its value is returned automatically
let sum = (a, b) => a + b;
// multi-line body with { ... } needs an explicit return
let sum2 = (a, b) => {
// ...
return a + b;
};
// no arguments — keep the empty parentheses
let sayHi = () => alert("Hello");
// exactly one argument — parentheses optional
let double = n => n * 2;
A few rules that hold across all three:
- Local variables — anything declared in the body, plus the parameters, is visible only inside the function.
- Default parameters — give a parameter a fallback with
=, as infunction sum(a = 1, b = 2) {...}. The default kicks in when the argument is missing orundefined. - Always a return value — a function that never hits a
return(or hits a barereturn;) still returns something:undefined.
Details: see Functions, Arrow functions, the basics.
More to come
That’s the whole First Steps toolkit on one page. Everything so far is the foundation — the syntax and behavior you’ll lean on constantly. From here the course digs into the parts that make JavaScript genuinely powerful: objects, prototypes, closures, and the async model. The specials and sharp edges keep coming, and now you have the base to make sense of them.