Advanced Web Programming
JavaScript Values, Operators and Control Flow
PGCP-AC
JavaScript programs operate on values, store them in bindings, combine them with operators and choose which statements execute. These fundamentals determine the result of every later DOM, React and Node.js program.
1. Values and bindings
JavaScript has primitive values including numbers, strings, booleans, undefined, null, bigint and symbols. Objects contain properties and have identity. let creates a block-scoped binding that can be reassigned; const prevents reassignment of the binding, but does not make a referenced object immutable. var uses function or global scope rather than block scope. Reading a lexical binding before initialization falls within the temporal dead zone.
Operators may convert operands. The expression "5" + 2 concatenates to "52", whereas "5" - 2 produces numeric 3. Strict equality avoids the coercions performed by loose equality. NaN represents an invalid numeric result and is not equal to itself; Number.isNaN checks specifically for that value. Falsy values include false, zero, empty string, null, undefined and NaN. Empty arrays and empty objects are truthy.
2. Flow and useful built-ins
Use if, switch and the conditional operator for selection; use for, while and do...while for iteration. break exits the nearest relevant loop or switch; continue advances to the next loop iteration. for...of consumes iterable values, whereas for...in enumerates enumerable property names. Strings are immutable, so methods return results rather than changing their characters in place. Numeric parsing should be followed by validation, especially when input may contain trailing characters.
const scores = [12, 8, 20];
let total = 0;
for (const score of scores) total += score;
console.log(total / scores.length); // 13.333...
JavaScript numbers use floating-point arithmetic, so decimal fractions may not have exact representations. Dates represent instants internally, but formatting and local accessors depend on timezone conventions. A date-only business value should be distinguished from a timestamp. These distinctions matter when displaying deadlines or calculating calendar differences.
3. Arrays, dates and conversion traps
The string method split produces an array, while an array's join produces a string. Slice selects a portion using an exclusive ending index. Includes checks membership and indexOf reports a matching position or minus one. To test whether a value is an array, use Array.isArray rather than typeof, since typeof arrays is object.
parseInt('12px', 10) produces 12 because it reads a valid initial integer portion; Number('12px') produces NaN because the complete text is not a numeric representation. Neither operation substitutes for a deliberate input policy. JavaScript Date's traditional numeric constructor uses zero-based months, so January is month zero. Local getters and UTC getters describe the same stored instant through different calendar frames. Strings should use a documented date format instead of relying on ambiguous day/month ordering.
4. Primitive values and objects
Primitive values are numbers, bigints, strings, booleans, symbols, undefined and null. A primitive is immutable: an operation can produce a new string, but it cannot replace a character inside the original string. Objects—including arrays and functions—have identity and can hold mutable properties.
const first = { score: 10 };
const second = { score: 10 };
const alias = first;
console.log(first === second); // false: different objects
console.log(first === alias); // true: same object
typeof null returns "object", a historical result rather than evidence that null is an ordinary object. typeof [] is also "object"; use Array.isArray(value) to recognize arrays.
5. Bindings, scope and the temporal dead zone
Prefer const when the binding will not be reassigned and let when reassignment is part of the algorithm. Const protects the binding, not the object:
const settings = { theme: 'light' };
settings.theme = 'dark'; // allowed
// settings = {}; // TypeError if executed
Both let and const are block-scoped. var is function-scoped, permits redeclaration and has older hoisting behavior that can hide mistakes.
if (true) {
let message = 'inside';
}
// console.log(message); // ReferenceError
A lexical binding exists from the beginning of its scope but cannot be read until its declaration is evaluated. That interval is the temporal dead zone. It prevents access from silently producing an uninitialized value.
6. Conversion and equality
JavaScript performs explicit conversion when code calls Number, String or Boolean and implicit coercion for certain operators.
| Expression | Result | Reason |
|---|---|---|
"5" + 2 | "52" | String concatenation |
"5" - 2 | 3 | Numeric conversion for subtraction |
Number("") | 0 | Empty string converts numerically to zero |
Boolean("false") | true | Every nonempty string is truthy |
null == undefined | true | Special loose-equality rule |
null === undefined | false | Different types under strict equality |
Use strict equality by default because it compares type and value without loose-equality conversion. Learn coercion rules to read existing code and avoid assuming that strict equality makes every comparison simple: object equality still compares identity and NaN === NaN is false.
The falsy values are false, numeric zero (including -0), 0n, the empty string, null, undefined and NaN. Arrays and objects are truthy even when empty.
7. Logical and nullish operators
&& and || short-circuit and return an operand, not necessarily a boolean:
const displayName = userInput || 'Anonymous';
const limit = suppliedLimit ?? 20;
The first expression replaces every falsy value, including "" and 0. Nullish coalescing replaces only null or undefined, so it is safer when zero is meaningful. Optional chaining, such as account.profile?.name, stops and yields undefined when the value before ?. is nullish.
8. Arithmetic and numeric edge cases
JavaScript's ordinary number type uses IEEE 754 floating-point representation. Some decimal fractions cannot be stored exactly:
console.log(0.1 + 0.2); // 0.30000000000000004
Do not test calculated decimals for exact equality when rounding error is possible. Monetary applications often store the smallest currency unit as an integer or use an appropriate decimal library.
% returns a remainder and ** performs exponentiation. Division by zero can produce positive or negative Infinity; invalid numeric operations can produce NaN. Number.isFinite and Number.isNaN avoid the coercion performed by older global checks.
9. Branching
Use if for general boolean conditions and switch when comparing one expression against several cases.
function shippingCost(method, total) {
if (total < 0) throw new RangeError('Total cannot be negative');
if (total >= 100) return 0;
switch (method) {
case 'standard': return 6;
case 'express': return 15;
default: throw new Error('Unknown shipping method');
}
}
Cases fall through until a break, return or exception changes control flow. Deliberate fall-through should be obvious and documented. The conditional operator is useful for a compact value choice, but nested conditional expressions quickly become difficult to read.
10. Loops and termination
| Construct | Best fit |
|---|---|
for | Initialization, condition and update belong together |
while | Repeat while a condition remains true |
do...while | Execute once before the first condition check |
for...of | Consume values from an iterable |
for...in | Enumerate enumerable property keys |
const marks = [72, 81, 65];
let total = 0;
for (const mark of marks) {
if (!Number.isFinite(mark)) continue;
total += mark;
}
continue skips the remainder of the current iteration; break exits the nearest loop. A return exits the entire function. Every loop should have a clear progress step or external event that eventually makes its condition false.
Do not use for...in as the usual array-value loop: it produces property names as strings and can include additional enumerable properties. Use for...of, an array method or an index loop according to the algorithm.
11. Parsing external input
Browser input arrives as text. Conversion must be intentional:
const raw = '42';
const quantity = Number(raw);
if (!Number.isInteger(quantity) || quantity < 1) {
throw new Error('Quantity must be a positive integer');
}
Number('12px') yields NaN because the full string is not numeric. parseInt('12px', 10) yields 12 because parsing stops at the first invalid character. Neither behavior is universally correct; select one based on the accepted input grammar. Always pass a radix to parseInt for clarity.
12. Arrays and strings
slice(start, end) copies a range whose end is exclusive and does not mutate the source. splice changes an array. includes tests membership, while indexOf returns a position or -1. split turns a string into pieces and join combines array elements into a string.
const csv = 'red,green,blue';
const colors = csv.split(',');
const firstTwo = colors.slice(0, 2);
console.log(firstTwo.join(' / ')); // red / green
Array assignment copies a reference, not the array elements. A shallow spread copy creates a new outer array but does not recursively clone nested objects.
13. Dates and time zones
A Date represents an instant as a numeric time value. Local getters and UTC getters view that instant through different calendar frames. The numeric multi-argument constructor uses zero-based months, so month 0 is January.
Distinguish a calendar-only value such as a birthday from an instant such as a transaction timestamp. Parsing ambiguous strings such as 04/05/2026 is unsafe because readers and environments may disagree about day and month. Use documented formats, include offsets for instants and format output deliberately for the user.
14. Worked trace
const values = ['4', 0, null, 6];
let sum = 0;
for (const value of values) {
const number = Number(value);
if (!Number.isFinite(number) || number === 0) continue;
sum += number;
}
console.log(sum); // 10
'4' converts to 4 and is added. Zero converts to zero and is skipped. null also converts to zero and is skipped. Six is added. The example shows why conversion and validation must be read together: Number(null) is valid numerically even when null may be invalid according to the application's data rules.
15. Practical considerations
- Const prevents binding reassignment, not object mutation.
typeof nullis"object"; use a direct null check.- Empty arrays and objects are truthy.
+may concatenate, while-performs numeric conversion.NaNis not equal to itself; useNumber.isNaN.for...ofproduces iterable values;for...inproduces property names.- A do-while body runs at least once.
- Strings are immutable.
- Floating-point decimal arithmetic can be inexact.
- Date constructor months are zero-based in the numeric form.
Continue learning
Related notes
Put this topic into timed practice
Open mock tests when you want full-exam pacing, or keep drilling in practice mode.