Skip to main content
Adapted, not copied

Original notes for JavaScript360.org — topic order inspired by Eloquent JavaScript (4th ed.) by Marijn Haverbeke. See the full attribution note.

Chapter 1: Values, Types & Operators

Estimated time: ~35 minutes · Chapter type: Concept

What you'll learn

  • The handful of primitive value types JavaScript actually has
  • How numbers work under the hood, and why 0.1 + 0.2 isn't 0.3
  • Strings, template literals, and the operators you'll use every day
  • JavaScript's much-mocked (and much-misunderstood) automatic type conversion

Values are the atoms of a program

Every program that does anything manipulates values — pieces of data like the number 42, the text "hello", or true. Every value in JavaScript has a type, and the type determines what you can do with it. There are only a handful of primitive types to learn: number, string, boolean, undefined, null, symbol, and bigint. Everything else you'll build (arrays, objects, functions) is a variation on the object type, which gets its own chapter.

You can always ask JavaScript what type a value is with the typeof operator:

typeof 42;        // "number"
typeof "hi"; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof null; // "object" — a decades-old bug that's now permanent, see below

Numbers

JavaScript has exactly one number type — there's no separate int vs float like in many other languages. Every number, whole or fractional, is stored as a 64-bit floating-point value, the IEEE 754 standard also used by most other modern languages.

let orders = 128;
let averageOrderValue = 47.5;
let distanceInMeters = 3.02e8; // scientific notation: 3.02 × 10^8

The floating-point representation is fast and flexible, but it can't represent every decimal fraction exactly — much like 1/3 can't be written exactly in decimal. That leads to the famous surprise:

0.1 + 0.2; // 0.30000000000000004

This isn't a JavaScript bug; it's how binary floating-point math works in essentially every mainstream language. The practical lesson: never compare floating-point numbers with === when you expect a "round" result — compare the difference against a small tolerance instead:

function roughlyEqual(a, b, epsilon = 1e-9) {
return Math.abs(a - b) < epsilon;
}
roughlyEqual(0.1 + 0.2, 0.3); // true

Arithmetic on numbers works as you'd expect: +, -, *, /, and % (remainder, not "modulo" in the strict mathematical sense once negative numbers are involved). There's also ** for exponentiation.

17 % 5;   // 2  — remainder after dividing 17 by 5
2 ** 10; // 1024

Two special numeric values are worth knowing early:

  • Infinity / -Infinity — what you get from dividing by zero, or a value too large to represent.
  • NaN ("Not a Number") — the result of an undefined numeric operation, like 0 / 0 or parsing "abc" as a number. NaN has the bizarre property that it's never equal to anything, including itself — use Number.isNaN(value) to test for it, never value === NaN.
NaN === NaN;          // false — surprising, but consistent with IEEE 754
Number.isNaN(NaN); // true — the correct way to check

Strings

Strings hold text, wrapped in single quotes, double quotes, or backticks. Backticks give you template literals, which let you embed expressions directly and span multiple lines — reach for these by default in modern code.

const first = 'Grace';
const last = "Hopper";
const bio = `${first} ${last} helped popularize the term "debugging."`;

Strings are effectively arrays of characters (technically, UTF-16 code units — more on the distinction in Chapter 9). You can index into them, check their length, and call a long list of built-in methods:

const word = 'JavaScript';
word.length; // 10
word[0]; // "J"
word.toUpperCase(); // "JAVASCRIPT"
word.slice(0, 4); // "Java"
word.includes('Script'); // true

Strings, like numbers, are immutable — none of these methods change the original string; they all return a new one. word.toUpperCase() doesn't touch word; you have to capture the return value if you want to keep it.

You can glue strings together with +, but template literals are almost always the cleaner choice once more than one value is involved:

const price = 19.99;
'The item costs $' + price + ' today.'; // works, but clunky
`The item costs $${price} today.`; // clearer

Unary operators

A unary operator takes a single value. You've already used one: typeof. Two others come up constantly:

  • -value negates a number.
  • !value flips a boolean — and coerces its operand to a boolean first if it isn't one already (see "truthy and falsy" below).
!true;       // false
!0; // true — 0 is "falsy"
!!'hello'; // true — double negation is a common idiom for "coerce to boolean"

Boolean values and comparisons

Boolean has exactly two values: true and false. You produce them with comparison operators (>, <, >=, <=, ===, !==) and combine them with logical operators:

  • && (AND) — true only if both sides are true
  • || (OR) — true if at least one side is true
  • ! (NOT) — flips a boolean

Crucially, && and || short-circuit: they stop evaluating as soon as the result is determined. This isn't just an optimization — it's a pattern you'll use constantly to write conditional logic compactly:

function greet(user) {
return user && user.name; // only reads user.name if user isn't null/undefined
}

const config = userSetting || defaultSetting; // fall back to a default

There's a third logical operator worth knowing from day one: ??, the nullish coalescing operator. Unlike ||, which falls back on any falsy value (0, "", false included), ?? only falls back when the left side is specifically null or undefined — which is usually what you actually meant:

const quantity = 0;
quantity || 10; // 10 — probably not what you wanted, 0 got treated as "empty"
quantity ?? 10; // 0 — correctly preserves an intentional zero

Empty values: undefined and null

JavaScript has two different "nothing" values, which trips up a lot of newcomers:

  • undefined means "this hasn't been given a value" — it's what a variable holds before assignment, what a missing function argument defaults to, and what you get accessing a property that doesn't exist.
  • null means "this is deliberately empty" — you assign it yourself to say "there's no value here, on purpose."
let assignee;
console.log(assignee); // undefined — nobody's been assigned yet

let winner = null; // explicitly: nobody has won (yet)

In practice, treat them as interchangeable "absence of value" signals in most everyday code, but understand the distinction when you read other people's code or API documentation — a function's docs might specifically say it returns null (not undefined) when a search finds nothing, for example.

Automatic type conversion

This is the part of JavaScript that generates the most jokes — and the most confusion. When you use an operator on values of mismatched types, JavaScript tries to convert one or both operands so the operation makes sense, rather than throwing an error. The classic example:

'5' + 1;    // "51"  — the number is converted to a string, then concatenated
'5' - 1; // 4 — the string is converted to a number, then subtracted
'five' - 1; // NaN — "five" can't become a number, so the result is NaN

The rule of thumb: + prefers strings (if either operand is a string, it concatenates), while every other arithmetic operator (-, *, /) prefers numbers and will try to convert strings to numbers.

This is exactly why you should almost always use === and !== (strict equality) instead of == and != (loose equality). Loose equality performs type conversion before comparing, and the rules are genuinely hard to memorize:

'' == 0;         // true
0 == false; // true
null == undefined; // true
null == 0; // false — yes, really

None of these are things you want to reason about mid-debugging session. Strict equality (===) skips all conversion — if the types differ, the values are simply not equal, full stop:

'' === 0;    // false
0 === false; // false

Rule for this course (and most production codebases): always use === / !==, and reach for == / != only in the one genuinely useful case — checking for "either null or undefined" with value == null.

Key takeaways

  • JavaScript has one number type (64-bit floating point), so decimal math can be slightly imprecise — never test floats for exact equality.
  • Strings are immutable; every string method returns a new string rather than modifying the original.
  • &&, ||, and ?? all short-circuit and are used constantly for defaults and conditional logic — know the difference between || and ??.
  • undefined means "no value was ever set"; null means "intentionally empty."
  • Prefer ===/!== over ==/!= to avoid JavaScript's sometimes-surprising automatic type conversion.

Try it yourself

  1. Without running it, predict the output of typeof (typeof 1). Then check your answer.
  2. Write a one-line expression using ?? that gives settings.timeout a default of 30 only when it's null or undefined (not when it's legitimately 0).
  3. Explain, in your own words, why '5' + 3 - 1 evaluates to 52 rather than 7. Walk through the two operators separately.
Hints
  1. typeof 1 is a string, and typeof on a string always gives the same answer.
  2. Nullish coalescing only falls through on null/undefined, not on falsy-but-real values like 0.
  3. Operators run left to right when they have equal precedence: figure out what '5' + 3 becomes before the second operator runs.

Summary

Values have types, and JavaScript's type system is looser than most — operators will silently convert types rather than error out. That flexibility is convenient in small scripts and a common source of bugs in large ones, which is why disciplined JavaScript code leans on ===, explicit conversions (Number(x), String(x)), and tools like TypeScript once a codebase grows. Next, we zoom out from individual values to how statements and control flow assemble them into actual programs.

 

🌟 Join the JavaScript360 community

Connect with fellow JS developers, and get free interview prep, React challenges, and DSA guides.

Join on WhatsApp →