JavaScript Programming FoundationsIntroductionJavaScript is the programming language that adds behaviour, interaction, and decision-making to web pages. HTML describes structure, CSS controls presentation, and JavaScript provides programmable logic. In a modern browser, JavaScript can respond to user actions, validate forms, update page content, communicate with servers, store data, and control multimedia. JavaScript is also used outside browsers in environments such as Node.js, making it relevant to both front-end and full-stack development. This chapter develops the programming foundations required to write reliable JavaScript programs. The emphasis is on syntax, values, variables, operators, type conversion, control flow, functions, scope, strict mode, and introductory algorithmic reasoning. The examples use standard JavaScript without external libraries. Learning ObjectivesBy the end of this chapter, you should be able to: - explain the role of JavaScript in web applications;
- declare variables using
let, const, and, where necessary, var; - distinguish primitive data types and understand values such as
undefined and null; - use arithmetic, comparison, logical, assignment, and conditional operators;
- predict and control implicit and explicit type conversion;
- construct conditional statements and loops;
- define and invoke functions, including functions with parameters and return values;
- explain scope, hoisting, closures at an introductory level, and strict mode;
- design simple algorithms using sequence, selection, and iteration;
- test, debug, and improve small JavaScript programs.
1. JavaScript as a Programming LanguageA program is a sequence of instructions that transforms input into output. JavaScript is dynamically typed: a variable does not have a permanently fixed type, although each value has a type. It is also generally interpreted or just-in-time compiled by a JavaScript engine. The engine reads source code, creates an execution environment, and evaluates statements and expressions. A JavaScript statement performs an action, such as declaring a variable or displaying a message. An expression produces a value. For example: let total = 4 + 6;
console.log(total); 4 + 6 is an expression whose value is 10; the declaration and output are statements. Semicolons are usually optional because JavaScript has automatic semicolon insertion, but using semicolons consistently can make statement boundaries clearer.
Identifiers are names chosen by the programmer. They may contain letters, digits, underscores, and dollar signs, but cannot begin with a digit or duplicate a reserved word. JavaScript is case-sensitive: score, Score, and SCORE are different identifiers. Descriptive names such as monthlyCost are preferable to vague names such as x. Comments document code and are ignored by the engine: // A single-line comment
/* A comment
spanning several lines */ 2. Variables and ConstantsA variable is a named reference to a value. Modern JavaScript provides let and const for declarations. let attempts = 0;
attempts = attempts + 1;
const taxRate = 0.15; let permits reassignment, whereas const does not permit reassignment of the binding. A const object can still have its properties changed because the binding, not necessarily the object’s contents, is constant:
const user = { name: "Amina" };
user.name = "Daniel"; // permitted
// user = { name: "Leah" }; // TypeErrorA variable declared but not assigned has the value undefined: let response;
console.log(response); // undefined Older JavaScript code may use var. It is function-scoped rather than block-scoped and has hoisting behaviour that can cause confusing results. New code should normally use const by default and let when reassignment is required. A useful practice is to declare a value close to where it is first used and to avoid unnecessary mutable state. This makes programs easier to read and reduces accidental changes. 3. Data Types and ValuesJavaScript values belong to primitive or object categories. The principal primitive types are shown below. | Type | Example | Description |
|---|
string | "Hello" | Textual data | number | 42, 3.14 | Integers and floating-point values | bigint | 9007199254740993n | Large integers with an n suffix | boolean | true, false | Logical truth values | undefined | undefined | No assigned value | null | null | Intentional absence of an object value | symbol | Symbol("id") | Unique identifiers |
Objects include arrays, functions, dates, and ordinary key–value structures. They can contain properties and methods: const product = {
name: "Keyboard",
price: 35
};
console.log(product.name);Arrays are ordered collections indexed from zero: const colours = ["red", "green", "blue"];
console.log(colours[0]); // red The typeof operator reports a value’s type, but it has a historical irregularity: typeof null returns "object". Therefore, checking for null should use strict equality, such as value === null. Strings and Template LiteralsStrings may use single quotes, double quotes, or backticks. Backticks support interpolation: const name = "Mira";
const message = `Welcome, ${name}.`;String values are immutable. Methods such as toUpperCase() return a new string rather than changing the original. Numbers and Special Numeric ValuesJavaScript uses the number type for most numeric work. It represents ordinary numbers using double-precision floating-point representation. Consequently, some decimal calculations are approximate: console.log(0.1 + 0.2); // 0.30000000000000004 For financial applications, software should avoid treating binary floating-point results as exact currency values. One common approach is to store the smallest currency unit as an integer, such as cents, or to use a suitable decimal arithmetic library. Special numeric values include NaN (“not a number”), Infinity, and -Infinity. Number.isNaN(value) is preferable to a loose comparison when checking for NaN. 4. Operators and ExpressionsOperators combine or transform values. Arithmetic OperatorsThe arithmetic operators are +, -, , /, %, and *. The remainder operator % returns the remainder after division; it is useful for testing divisibility or alternating behaviour. const remainder = 17 % 5; // 2
const square = 4 ** 2; // 16 The + operator performs addition when both operands are numeric, but it performs concatenation when either relevant operand is a string: console.log(2 + 3); // 5
console.log("2" + 3); // "23"Operator precedence determines evaluation order. Parentheses should be used when they improve clarity rather than relying on a reader’s memory of precedence rules. Assignment OperatorsAssignment stores a value in a variable. Compound forms combine an operation with assignment: let count = 10;
count += 2; // 12
count *= 3; // 36 Increment and decrement operators, ++ and --, change a numeric variable by one. Their prefix and postfix forms differ in the value produced by the expression, so explicit arithmetic is often clearer in complex statements. Comparison Operators=== and !== perform strict equality and inequality checks without automatic type conversion. == and != perform coercion and can produce surprising results. Professional code generally prefers strict comparison:
0 === "0"; // false
0 == "0"; // true Relational operators include <, >, <=, and >=. When both operands are strings, comparison is lexicographic rather than numeric. Convert input explicitly before numeric comparison. Logical OperatorsThe operators && (AND), || (OR), and ! (NOT) are used to combine conditions. They use short-circuit evaluation: A && B evaluates B only if A is truthy, while A || B evaluates B only if A is falsy. const hasPermission = true;
const isOwner = false;
const mayEdit = hasPermission || isOwner; These operators return an operand, not necessarily a Boolean. This behaviour supports default values but should be used carefully: const displayName = suppliedName || "Guest"; The nullish coalescing operator ?? uses the fallback only when the left side is null or undefined, not when it is 0, false, or an empty string. 5. Truthiness and Type ConversionIn a condition, JavaScript converts a value to a Boolean context. Falsy values include false, 0, -0, NaN, "", null, and undefined. Most other values, including empty arrays and empty objects, are truthy. Explicit conversion is safer than relying on implicit coercion: const text = "42";
const quantity = Number(text);
const label = String(quantity);
const available = Boolean(quantity); Number("42") produces 42, while Number("hello") produces NaN. parseInt() and parseFloat() can parse numeric prefixes, but a radix should be supplied to parseInt when appropriate:
const wholeNumber = parseInt("101", 10);
const decimalNumber = parseFloat("12.5");Browser form controls commonly provide strings even when the user enters digits. Therefore, this code is potentially incorrect: const first = "10";
const second = "5";
console.log(first + second); // "105" The corrected version converts the input: console.log(Number(first) + Number(second)); // 15 Conversion should also be validated. Number.isNaN(value) can determine whether a numeric conversion failed, and range checks can ensure that a valid number is also acceptable for the application. 6. Conditional LogicConditional statements allow a program to select actions. const mark = 72;
if (mark >= 70) {
console.log("Distinction");
} else if (mark >= 50) {
console.log("Pass");
} else {
console.log("Resubmit");
}Conditions should express business rules clearly. If several branches compare one expression with fixed values, switch may be appropriate: switch (status) {
case "pending":
console.log("Awaiting review");
break;
case "approved":
console.log("Ready");
break;
default:
console.log("Unknown status");
}The break statements prevent fall-through. Fall-through can be intentional, but it should be documented. The conditional, or ternary, operator is suitable for a short choice: const message = mark >= 50 ? "Pass" : "Fail"; Nested ternary expressions usually reduce readability and should be replaced with ordinary if...else logic. 7. Loops and RepetitionLoops repeat instructions. A for loop is suitable when a counter or predictable range is involved: let total = 0;
for (let i = 1; i <= 5; i += 1) {
total += i;
}
console.log(total); // 15A while loop is appropriate when repetition depends on a condition whose number of iterations is not known in advance: let attempts = 0;
while (attempts < 3) {
attempts += 1;
}A do...while loop executes its body at least once. for...of iterates over values in an iterable, such as an array: const prices = [12, 18, 25];
for (const price of prices) {
console.log(price);
}for...in iterates over property names and is generally more suitable for object properties than array values.
break exits a loop, and continue skips to the next iteration. Both can be useful, but excessive use may make control flow difficult to follow. Every while loop must make progress toward termination; otherwise it may become an infinite loop.
Array Methods as Iteration ToolsModern JavaScript provides expressive methods: const prices = [10, 20, 30];
const discounted = prices.map(price => price * 0.9);
const expensive = prices.filter(price => price > 15);
const total = prices.reduce((sum, price) => sum + price, 0); map creates a transformed array, filter selects elements, and reduce combines elements into one result. These methods can improve readability when the operation is naturally a transformation, selection, or accumulation.
8. Functions and Reusable LogicA function packages instructions behind a name. It may accept parameters and return a value. function calculateSubtotal(price, quantity) {
return price * quantity;
}
const subtotal = calculateSubtotal(12.5, 4);Parameters are local names; arguments are the values supplied during invocation. A function without an explicit return produces undefined. Good functions generally have a clear purpose, limited side effects, and names that describe their action. Function expressions and arrow functions are alternatives: const square = function (number) {
return number * number;
};
const cube = number => number ** 3;An arrow function with one expression returns that expression implicitly. With multiple statements, braces are required and an explicit return is normally needed. Default parameters provide values when an argument is omitted: function greet(name = "Guest") {
return `Hello, ${name}`;
}Functions can be passed as values. This supports callbacks, event handling, and array methods. A callback is a function supplied to another function for later execution. Pure Functions and Side EffectsA pure function returns the same output for the same inputs and does not change external state. For example, calculateSubtotal is pure. A function that modifies a global variable, changes the page, or sends a network request has side effects. Pure functions are easier to test; side effects are often necessary in web applications but should be isolated. 9. Scope, Hoisting, and Strict ModeScope determines where an identifier can be accessed. JavaScript has global scope, function scope, and block scope. Variables declared with let and const are block-scoped: if (true) {
const internalCode = 7;
console.log(internalCode);
}
// console.log(internalCode); // ReferenceErrorA function can access variables in its surrounding lexical environment. This is the basis of closures. A closure occurs when a function retains access to variables from the scope in which it was created: function makeCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const nextCount = makeCounter();
console.log(nextCount()); // 1
console.log(nextCount()); // 2The returned function retains count even after makeCounter has finished. Closures are useful for private state, event handlers, and modular design. Hoisting describes the processing of declarations before execution. Function declarations can generally be called before their position in source code. let and const declarations are also registered, but they cannot be accessed before their declaration; this interval is called the temporal dead zone. var is initialized with undefined, which is one reason it can conceal errors. Strict mode applies stricter language rules: "use strict"; It prevents accidental creation of global variables, rejects some unsafe syntax, and changes certain this behaviours. JavaScript modules are automatically strict, and strict mode is recommended for scripts that are not modules. 10. Algorithmic Problem-SolvingAn algorithm is a finite, ordered method for solving a problem. Before coding, clarify the input, output, rules, and exceptional cases. A practical design process is: - State the problem precisely.
- Identify inputs and required outputs.
- Break the solution into sequence, selection, and iteration.
- Write pseudocode or a small example by hand.
- Implement the algorithm.
- Test normal, boundary, and invalid cases.
- Refactor for clarity and maintainability.
Worked Example: Grade ClassificationProblem. Given a mark from 0 to 100, return a classification: distinction for 70 or above, pass for 50–69, and fail below 50. Invalid marks must be rejected. Pseudocode: If mark is not numeric or is outside 0–100, report invalid input.
Else if mark ≥ 70, return distinction.
Else if mark ≥ 50, return pass.
Else return fail. Implementation: function classifyMark(mark) {
if (typeof mark !== "number" || !Number.isFinite(mark) || mark < 0 || mark > 100) {
return "Invalid mark";
}
if (mark >= 70) return "Distinction";
if (mark >= 50) return "Pass";
return "Fail";
}The order of tests matters. Once a mark is known to be at least 70, it must not be classified as merely a pass. The validation also rejects NaN and infinite values. Worked Example: Finding the Largest ValueThe following algorithm maintains a running maximum: function largest(values) {
if (values.length === 0) {
return undefined;
}
let maximum = values[0];
for (const value of values) {
if (value > maximum) {
maximum = value;
}
}
return maximum;
}For [4, 9, 2, 7], the running maximum is 4, then 9; the remaining values do not exceed 9, so the result is 9. The function explicitly defines the empty-array case rather than attempting to access a nonexistent first element. The loop examines each element once. If there are n values, the number of comparisons grows approximately in proportion to n; this is commonly described as linear time, or O(n). The additional storage used apart from the input is constant, described as O(1). These are introductory complexity descriptions, not execution-time measurements in seconds. 11. Applied Case: A Small Shopping-Cart CalculationBackgroundA small online retailer needs a browser-side function to calculate an order subtotal. Each item has a price and quantity. Orders with a subtotal of at least 100 receive a 10% discount. Invalid prices or quantities must not silently produce an incorrect total. Problem AnalysisThe function needs an array of item records. It must validate that prices are finite and non-negative, quantities are positive integers, and the item list is usable. It should calculate the subtotal first, then apply the discount rule. Currency rounding should be handled deliberately rather than assumed to be exact because JavaScript numbers use binary floating-point representation. Approach and Solutionfunction calculateOrder(items) {
if (!Array.isArray(items)) {
throw new TypeError("Items must be an array");
}
let subtotal = 0;
for (const item of items) {
if (!Number.isFinite(item.price) || item.price < 0 ||
!Number.isInteger(item.quantity) || item.quantity <= 0) {
throw new RangeError("Invalid item");
}
subtotal += item.price * item.quantity;
}
const discount = subtotal >= 100 ? subtotal * 0.10 : 0;
const total = subtotal - discount;
return { subtotal, discount, total };
}For items priced at 40 and 70 with quantities 1 and 1, the subtotal is 110. The discount is 11, so the total is 99. In production, the business should specify whether prices are stored as cents, whether tax is applied before or after discount, and how totals are rounded. The example deliberately focuses on programming structure rather than claiming that one currency policy is universally correct. Outcome and Learning PointsThe function separates validation, accumulation, and decision-making. It rejects malformed data instead of returning a misleading total. In a complete web application, the server must repeat validation because browser-side code can be modified or bypassed. The case also demonstrates why business rules should be written explicitly and tested with boundary values such as 99.99, 100, zero quantity, and invalid input. 12. Testing, Debugging, and Professional PracticeA program is not complete merely because it runs once. Test cases should include: - ordinary valid inputs;
- boundary values, such as 0, 50, 70, and 100;
- empty arrays and missing values;
- invalid types and malformed strings;
- unusually large values;
- repeated calls to functions that retain state.
console.log() is useful for temporary inspection, while browser developer tools provide breakpoints, call stacks, watch expressions, and network inspection. A clear error message should identify what failed without exposing confidential information.
Common beginner errors include using = instead of === in a comparison, forgetting to convert form strings to numbers, creating an infinite loop, assuming arrays are one-indexed, and relying on implicit coercion. Linters and formatters can identify many style and correctness problems, but they do not replace reasoning and tests. Security is also relevant. Never insert untrusted text into a page with innerHTML without appropriate sanitisation. Prefer textContent for plain text. Validate data on the server as well as in the browser, avoid exposing secrets in client-side JavaScript, and treat all user input as untrusted. 13. Contemporary Relevance and LimitationsJavaScript now supports modules, classes, asynchronous programming, and extensive platform APIs. These topics build on the foundations in this chapter. Modern applications often use import and export, and modules operate in strict mode automatically. JavaScript’s event-driven model is central to interactive interfaces, but asynchronous operations require concepts such as promises and async/await, which should be studied next. JavaScript’s flexibility is productive but can also create defects. Dynamic typing permits rapid development, yet errors may appear at runtime. TypeScript adds static type checking and is widely used in professional projects, but it does not eliminate the need to understand JavaScript’s runtime behaviour. Frameworks and AI-assisted coding tools can accelerate development, but generated code must be reviewed, tested, and checked for security, licensing, privacy, and correctness. A developer remains responsible for the software deployed to users. Best Practices- Prefer
const; use let only when reassignment is required. - Prefer strict equality (
===, !==) unless coercion is intentional and documented. - Convert and validate external input explicitly.
- Keep functions small, focused, and understandable.
- Use descriptive names and consistent formatting.
- Handle empty, invalid, and boundary cases.
- Avoid accidental global variables and enable strict mode or use modules.
- Isolate side effects from calculation logic.
- Test both normal behaviour and failure behaviour.
- Repeat security-sensitive validation on the server.
Chapter SummaryJavaScript provides the programmable layer of web technologies. Programs are constructed from expressions and statements using variables, values, operators, conditions, loops, and functions. const and let provide modern declaration semantics, while primitive types and objects represent application data. Explicit conversion and strict comparison reduce errors caused by JavaScript’s coercion rules. Conditional statements implement selection, and loops implement repetition. Functions support reuse, testing, and abstraction; pure functions are particularly predictable. Scope controls identifier visibility, closures preserve access to surrounding variables, and strict mode prevents several unsafe practices. Algorithmic problem-solving begins with precise inputs, outputs, validation, pseudocode, and systematic testing. Practical web development also requires attention to floating-point currency issues, security, server-side validation, maintainability, and ethical use of automated coding tools. Key TermsAlgorithm: A finite, ordered procedure for solving a problem. Argument: A value supplied when a function is called. Block scope: Visibility limited to a pair of braces for let and const declarations. Callback: A function passed to another function for later execution. Closure: A function together with access to variables in its surrounding lexical scope. Coercion: Automatic or implicit conversion between value types. Constant binding: A const reference that cannot be reassigned. Expression: Code that produces a value. Falsy: A value that becomes false in a Boolean context. Function: A reusable unit of executable logic. Hoisting: The processing of declarations before parts of code execute. Iteration: Repeated execution of instructions. Lexical scope: Scope determined by where code is written. Primitive: A non-object value such as a string, number, or Boolean. Pure function: A function without observable side effects whose output depends only on its inputs. Scope: The region in which an identifier can be accessed. Short-circuit evaluation: Stopping logical evaluation when the result is already determined. Strict mode: A JavaScript mode that applies stricter parsing and runtime rules. Type conversion: Changing a value from one data type to another. Truthy: A value that becomes true in a Boolean context. Review QuestionsShort Answer Questions- What is the difference between
let and const? - Why is strict equality generally preferred to loose equality?
- Name three falsy JavaScript values.
- What is the purpose of a function parameter and a return value?
- What problem can occur when form input is used in arithmetic without conversion?
Long Answer Questions- Explain JavaScript’s principal primitive data types and contrast them with objects. Include suitable examples.
- Describe
if...else, switch, and the conditional operator, and explain when each is appropriate. - Explain function scope, block scope, hoisting, and closures using examples.
- Discuss explicit and implicit type conversion and recommend practices for handling user input.
- Explain how loops and array methods support iteration, including the risks of infinite loops and unclear control flow.
Analytical / Application Questions- Design and implement a function that receives an array of numbers and returns the average, explicitly defining how an empty array and invalid values are handled.
- A form supplies price and quantity as strings. Analyse the errors that could arise if the strings are multiplied or added without validation, then propose a robust solution.
- Develop an algorithm for a login attempt counter that permits three attempts, reports lockout after the limit, and avoids accidental global variables. Explain your boundary tests.
Multiple Choice Questions with AnswersQuestion 1Which declaration is normally preferred for a value that will not be reassigned? A. const B. let C. var D. static Correct Answer: A. const Question 2What is the result of 0 === "0" in JavaScript? A. true B. false C. undefined D. NaN Correct Answer: B. false Question 3Which operator returns the remainder of a division? A. / B. ** C. % D. // Correct Answer: C. % Question 4What does Number("hello") produce? A. 0 B. false C. undefined D. NaN Correct Answer: D. NaN Question 5Which loop is most suitable for iterating directly over the values of an array? A. for...of B. for...in C. switch D. do...if Correct Answer: A. for...of Question 6What does a function return when it reaches the end without a return statement? A. null B. false C. undefined D. 0 Correct Answer: C. undefined Question 7Which value is falsy in a Boolean context? A. [] B. {} C. "hello" D. 0 Correct Answer: D. 0 Question 8What is the main effect of strict mode? A. It converts all strings to numbers B. It applies stricter language rules and rejects some unsafe behaviour C. It makes every variable globally scoped D. It disables functions Correct Answer: B. It applies stricter language rules and rejects some unsafe behaviour Question 9For an array with four elements, at which index is the first element stored? A. 0 B. 1 C. -1 D. 4 Correct Answer: A. 0 Question 10What does the array method filter return? A. A single accumulated value B. A new array containing elements that satisfy a condition C. The array’s final index D. A Boolean indicating whether every element matches Correct Answer: B. A new array containing elements that satisfy a condition
|