02 · Variables, Data Types & Operators¶
Declaring variables: let, const, var¶
let age = 25; // block-scoped, reassignable
const name = "Ada"; // block-scoped, cannot be reassigned
var height = 1.68; // function-scoped — legacy, avoid in new code
Prefer const by default, and let when you know the value needs to change.
Avoid var — its function-level (rather than block-level) scoping is a common
source of bugs, covered more in Module 4.
const isStudent = false;
age = 26; // fine — let allows reassignment
// name = "Grace"; // TypeError: Assignment to constant variable.
Primitive data types¶
const wholeNumber = 42; // number (JS has one numeric type)
const piIsh = 3.14159; // also number
const message = "hi there"; // string
const flag = true; // boolean
const nothing = null; // intentional "no value"
let notAssignedYet; // undefined — declared but not assigned
const big = 9007199254740993n; // BigInt, for integers beyond Number.MAX_SAFE_INTEGER
console.log(typeof wholeNumber); // "number"
console.log(typeof message); // "string"
console.log(typeof nothing); // "object" — a famous long-standing JS quirk
console.log(typeof notAssignedYet); // "undefined"
Numeric operators¶
const a = 7;
const b = 2;
console.log(a + b); // 9 addition
console.log(a - b); // 5 subtraction
console.log(a * b); // 14 multiplication
console.log(a / b); // 3.5 division (always float-capable)
console.log(a % b); // 1 modulo (remainder)
console.log(a ** b); // 49 exponentiation
console.log(Math.floor(a / b)); // 3 floor division equivalent
Comparison & logical operators¶
console.log(5 > 3); // true
console.log(5 == "5"); // true — loose equality, coerces types
console.log(5 === "5"); // false — strict equality, checks type too (prefer this)
console.log(5 !== "5"); // true
console.log(true && false); // false
console.log(true || false); // true
console.log(!true); // false
Always prefer === and !== over == and != — loose equality's coercion
rules are a frequent source of bugs.
Type conversion¶
String(42); // "42"
Number("42"); // 42
Number("3.5"); // 3.5
parseInt("42px"); // 42 — parses leading digits, ignores the rest
Boolean(0); // false
Boolean(""); // false
Boolean("x"); // true — any non-empty string is truthy
Boolean(null); // false
Boolean(undefined); // false
Truthy and falsy values¶
// Falsy: false, 0, "", null, undefined, NaN — everything else is truthy
if ("hello") {
console.log("strings with content are truthy");
}
Naming rules & convention¶
- Names use
camelCaseby convention (userAge, notUserAgeoruser_age). - Must start with a letter,
_, or$— can't start with a digit. - Constants that never change are often written
UPPER_SNAKE_CASE(const MAX_RETRIES = 3;) by convention, thoughconstalone doesn't imply this style.
| Keyword | Reassignable? | Scope |
|---|---|---|
const |
No | block |
let |
Yes | block |
var |
Yes | function (avoid) |
How It Actually Works¶
let and const aren't just "block-scoped var" — they exist because of the Temporal
Dead Zone (TDZ). Every let/const binding is hoisted to the top of its block during
compilation (V8 allocates the binding slot up front), but it stays uninitialized until
the declaration line actually executes. Reading it before that throws
ReferenceError: Cannot access 'x' before initialization — not because the variable
doesn't exist yet, but because the engine is deliberately blocking access to a slot it
already knows about. var, by contrast, is hoisted and initialized to undefined
immediately, which is why console.log(x); var x = 1; silently prints undefined
instead of throwing.
At the representation level, V8 doesn't store every value the same way. Small integers
get packed into a tagged Smi (small integer) representation directly in the pointer
word — no heap allocation at all. Once a number needs a fraction or exceeds the Smi
range, V8 boxes it as a HeapNumber. Objects get a hidden class (also called a
"map") the moment they're created, which records the order and types of their
properties; two objects created with properties added in the same order share a hidden
class and can reuse the same optimized machine code for property access. This is a
big part of why {a:1, b:2} is faster to work with than an object you build by adding
properties in unpredictable order later.
🔀 See this in another language¶
Exercise¶
Write a script that stores a rectangle's width and height, computes its
area and perimeter, and prints both formatted to 2 decimal places using
.toFixed(2).