HTML, CSS, JavaScript Basics

Chapter 10
Arrays, Objects and Modern JavaScript

Bachelors Program in Full Stack Development
HTML, CSS, JavaScript Basics   •   Bachelor

Arrays, Objects and Modern JavaScript

Introduction

Web applications rarely manipulate isolated values. They work with collections of products, user profiles, orders, messages, and configuration settings. JavaScript provides several related mechanisms for representing and transforming such information: strings for textual data, arrays for ordered collections, objects for named properties, JSON for data interchange, and modern language features that make data processing clearer and safer.

This chapter develops a practical and conceptual model of these mechanisms. It explains how data is represented, how iteration methods transform it, and how features such as destructuring, spread syntax, classes, and built-in objects support maintainable full-stack applications. The examples use standard JavaScript and assume familiarity with variables, functions, conditionals, and basic browser programming.

Learning Objectives

By the end of this chapter, students should be able to:

  • distinguish strings, arrays, objects, and JSON;
  • create, access, update, and transform arrays and objects;
  • choose suitable iteration methods such as `forEach`, `map`, `filter`, `find`, `some`, `every`, and `reduce`;
  • use destructuring, spread syntax, optional chaining, and nullish coalescing;
  • explain references, mutation, shallow copying, and immutability;
  • serialize and parse data using JSON;
  • define and use JavaScript classes;
  • apply built-in objects such as `Date`, `Math`, `Set`, and `Map` appropriately;
  • evaluate design, performance, security, and data-quality considerations in practical applications.

1. Data Representation in JavaScript

1.1 Strings

A string is an ordered sequence of characters represented by single quotes, double quotes, or backticks:

```js

const name = 'Amina';

const greeting = `Welcome, ${name}`;

```

Template literals, written with backticks, support interpolation and multiline text. Strings are immutable: an operation produces a new string rather than modifying the original string.

```js

const word = 'frontend';

const upper = word.toUpperCase();

console.log(word); // frontend

console.log(upper); // FRONTEND

```

Useful methods include `trim()`, `toLowerCase()`, `toUpperCase()`, `includes()`, `startsWith()`, `endsWith()`, `slice()`, `replace()`, and `split()`. A method such as `split(',')` converts a string into an array, while `join(', ')` converts an array into a string.

```js

const input = 'html, css, javascript';

const technologies = input.split(',').map(item => item.trim());

const display = technologies.join(' | ');

```

String comparison is case-sensitive and does not automatically express human language rules. For user-facing search, normalization, locale-aware comparison, or more sophisticated text processing may be required.

1.2 Arrays

An array is an ordered, zero-indexed collection. It may contain values of different types, although a consistent element type is usually easier to understand and maintain.

```js

const scores = [78, 91, 84];

console.log(scores[0]); // 78

console.log(scores.length); // 3

```

Arrays are objects with special behavior for indexed elements and a `length` property. Common mutating methods include `push`, `pop`, `shift`, `unshift`, `splice`, `sort`, and `reverse`. Non-mutating methods include `slice`, `concat`, `map`, `filter`, `find`, and `includes`.

The distinction between mutation and non-mutation is important. `push()` changes the existing array, whereas `concat()` returns another array. In user-interface applications, avoiding unnecessary mutation often makes state changes more predictable.

1.3 Objects

An object stores properties associated with keys. Keys are commonly strings or symbols, and values may be any JavaScript value, including functions and other objects.

```js

const student = {

id: 101,

name: 'Amina',

active: true

};

console.log(student.name);

console.log(student['id']);

```

Dot notation is concise when the property name is known and is a valid identifier. Bracket notation is required for computed property names or keys containing spaces:

```js

const property = 'name';

console.log(student[property]);

```

Properties can be added, updated, or removed:

```js

student.email = '[email protected]';

student.active = false;

delete student.email;

```

Objects may contain methods. Inside a method, `this` normally refers to the object through which the method is called, although the value of `this` depends on the invocation context.

2. References, Mutation, and Copying

Primitive values such as strings, numbers, booleans, `null`, `undefined`, `bigint`, and symbols are copied by value. Arrays and ordinary objects are reference values. Assigning an object variable to another variable copies the reference, not an independent object.

```js

const original = { count: 1 };

const alias = original;

alias.count = 2;

console.log(original.count); // 2

```

A shallow copy can be created with spread syntax or `Object.assign`:

```js

const copy = { ...original };

copy.count = 3;

```

However, shallow copying does not clone nested objects:

```js

const profile = { name: 'Amina', address: { city: 'Lagos' } };

const profileCopy = { ...profile };

profileCopy.address.city = 'Abuja';

console.log(profile.address.city); // Abuja

```

For structured data, `structuredClone()` can create a deep copy of many built-in data types, but it does not support every JavaScript value, such as functions. JSON serialization is not a general-purpose cloning mechanism because it loses types such as `Date`, `Map`, `Set`, `undefined`, and functions.

Immutability does not mean that data can never change. It means that a transformation creates a new value rather than unexpectedly modifying shared state. This approach is valuable in component-based interfaces, undo systems, testing, and concurrent data flows.

3. Iteration and Array Transformation

Modern JavaScript encourages expressing what should happen to a collection rather than managing every index manually. Nevertheless, a traditional `for...of` loop remains useful when early exit, asynchronous control, or complex procedural logic is needed.

3.1 `forEach`

`forEach` executes a callback once for each element. It is suitable for side effects such as logging or updating a separate system. It does not create a useful transformed array and cannot be stopped with `break`.

```js

const names = ['Amina', 'Jon', 'Li'];

names.forEach((name, index) => {

console.log(`${index}: ${name}`);

});

```

3.2 `map`

`map` creates a new array with one output for every input element. It is appropriate when the transformation preserves the number of elements.

```js

const prices = [10, 20, 30];

const withTax = prices.map(price => price * 1.15);

// [11.5, 23, 34.5]

```

The callback receives the element, its index, and the source array. A common error is using `map` when no returned value is needed.

3.3 `filter`

`filter` returns the elements for which the callback produces a truthy result. The output may contain fewer elements or none.

```js

const products = [

{ name: 'Keyboard', stock: 8 },

{ name: 'Mouse', stock: 0 }

];

const available = products.filter(product => product.stock > 0);

```

3.4 `find` and `findIndex`

`find` returns the first matching element or `undefined` when no element matches. `findIndex` returns the first matching index or `-1`.

```js

const account = products.find(product => product.name === 'Mouse');

```

These methods are preferable to manually searching when the intention is to locate one record.

3.5 `some` and `every`

`some` tests whether at least one element satisfies a condition. `every` tests whether all elements satisfy it. Both return booleans and can stop early.

```js

const hasUnavailable = products.some(product => product.stock === 0);

const allListed = products.every(product => product.name.length > 0);

```

3.6 `reduce`

`reduce` combines array elements into one accumulated result. The accumulator may be a number, string, array, object, or another structure. An initial value should normally be supplied because it establishes the accumulator’s type and handles empty arrays safely.

```js

const amounts = [12, 8, 10];

const total = amounts.reduce((sum, amount) => sum + amount, 0);

// 30

```

A grouped object can also be built:

```js

const orders = [

{ status: 'paid', total: 40 },

{ status: 'pending', total: 25 },

{ status: 'paid', total: 15 }

];

const totalsByStatus = orders.reduce((result, order) => {

result[order.status] = (result[order.status] ?? 0) + order.total;

return result;

}, {});

```

`reduce` is powerful but can become difficult to read when it performs many unrelated operations. Several clear transformations are often better than one opaque expression.

3.7 Sorting and comparison

`sort()` mutates an array and, by default, compares values as strings. Numeric sorting therefore requires a comparator:

```js

const values = [10, 2, 30];

values.sort((a, b) => a - b); // [2, 10, 30]

```

For objects:

```js

products.sort((a, b) => a.stock - b.stock);

```

The comparator should return a negative value when `a` precedes `b`, zero when their order is equivalent, and a positive value when `a` follows `b`. If the original order must be preserved, copy first: `const sorted = [...products].sort(...)`.

4. Destructuring and Spread Syntax

Destructuring extracts values from arrays or properties from objects into variables.

```js

const user = { id: 7, name: 'Amina', role: 'editor' };

const { name, role } = user;

const { name: displayName } = user;

```

Array destructuring is position-based:

```js

const rgb = [12, 80, 200];

const [red, green, blue] = rgb;

```

Defaults apply when a value is `undefined`:

```js

const settings = { theme: 'dark' };

const { theme, language = 'en' } = settings;

```

Destructuring is especially useful in function parameters:

```js

function describeProduct({ name, stock }) {

return `${name}: ${stock} available`;

}

```

Spread syntax expands iterable values in arrays or enumerable own properties in objects:

```js

const first = [1, 2];

const combined = [...first, 3, 4];

const base = { theme: 'light', size: 'medium' };

const customized = { ...base, theme: 'dark' };

```

When object properties overlap, later properties take precedence. Spread is shallow and should not be mistaken for deep cloning.

Rest syntax uses the same three dots but collects remaining values:

```js

const [head, ...tail] = [1, 2, 3, 4];

const { id, ...publicData } = user;

```

5. Safe Access and Defaulting

Optional chaining (`?.`) safely accesses a property or calls a method when an earlier value may be `null` or `undefined`.

```js

const city = user.address?.city;

```

The expression produces `undefined` instead of throwing an error when `address` is absent. Nullish coalescing (`??`) supplies a default only when the left side is `null` or `undefined`:

```js

const pageSize = settings.pageSize ?? 20;

```

This differs from `||`, which also replaces valid falsy values such as `0`, `false`, and an empty string. Use `??` when those values are meaningful.

6. JSON and Data Interchange

JSON, or JavaScript Object Notation, is a text format widely used by APIs and configuration systems. It supports objects, arrays, strings, numbers, booleans, and `null`. JSON does not support comments, functions, `undefined`, symbols, or class instances as such.

`JSON.stringify` converts a JavaScript value into JSON text, and `JSON.parse` converts valid JSON text into a JavaScript value.

```js

const order = { id: 18, items: ['book'], paid: true };

const text = JSON.stringify(order);

const restored = JSON.parse(text);

```

Parsing untrusted or malformed text can throw an exception, so boundary code should handle errors:

```js

function parseSettings(text) {

try {

return JSON.parse(text);

} catch {

return null;

}

}

```

JSON is a serialization format, not a validation system. An application must still validate required fields, types, ranges, authorization rules, and business constraints. Data received from a client must never be trusted merely because it is valid JSON.

7. Classes and Object-Oriented Structure

A class is a syntax for defining objects that share behavior and initialization logic. Classes may contain a `constructor`, instance methods, static methods, getters, setters, and private fields.

```js

class ShoppingCart {

#items = [];

add(product, quantity = 1) {

this.#items.push({ product, quantity });

}

get totalItems() {

return this.#items.reduce((sum, item) => sum + item.quantity, 0);

}

get items() {

return this.#items.map(item => ({ ...item }));

}

}

const cart = new ShoppingCart();

cart.add('Keyboard', 2);

console.log(cart.totalItems); // 2

```

The `#items` field is private at the language level. The `items` getter returns copies of entries rather than exposing the internal array directly. This is an example of encapsulation: internal representation is protected by a public interface.

Inheritance is available through `extends` and `super`, but it should be used when there is a genuine “is-a” relationship. Composition—combining smaller objects or functions—is often more flexible. Classes are not automatically superior to factory functions or modules; the choice should reflect the domain and required behavior.

8. Selected Built-in Objects

`Date`

`Date` represents a point in time as milliseconds from the ECMAScript epoch, 1 January 1970 UTC. Its display methods can use local time zones, so applications should be explicit about time-zone requirements. For example, `toISOString()` produces a UTC-based representation.

```js

const created = new Date();

console.log(created.toISOString());

```

Dates received from users or APIs should be validated. For complex calendar and time-zone operations, the modern Temporal proposal or a carefully selected library may be preferable where supported.

`Math`

`Math` provides numeric constants and functions such as `round`, `floor`, `ceil`, `max`, `min`, and `random`. `Math.random()` is not suitable for passwords, tokens, or security-sensitive identifiers; use the Web Crypto API for those purposes.

`Set`

A `Set` stores unique values and is useful for membership testing and deduplication:

```js

const tags = ['js', 'web', 'js'];

const uniqueTags = [...new Set(tags)];

```

`Map`

A `Map` stores key-value pairs and permits keys of any value type. It is useful when keys are not naturally strings or when frequent insertion, lookup, and deletion are central operations.

```js

const visits = new Map();

visits.set('home', 12);

visits.set('about', 5);

console.log(visits.get('home'));

```

`Object.keys`, `Object.values`, and `Object.entries`

These methods provide controlled iteration over an object’s enumerable own properties:

```js

const counts = { approved: 4, pending: 2 };

Object.entries(counts).forEach(([status, count]) => {

console.log(status, count);

});

```

9. Worked Example: Transforming an Order Dataset

Suppose an online bookstore receives the following data:

```js

const orders = [

{ id: 1, customer: 'Amina', status: 'paid', items: [{ price: 25, quantity: 2 }] },

{ id: 2, customer: 'Jon', status: 'pending', items: [{ price: 40, quantity: 1 }] },

{ id: 3, customer: 'Amina', status: 'paid', items: [{ price: 15, quantity: 3 }] }

];

```

The first task is to calculate each order’s value. `reduce` is appropriate because each order’s item array must be combined into one number:

```js

const enrichedOrders = orders.map(order => {

const total = order.items.reduce(

(sum, item) => sum + item.price * item.quantity,

0

);

return { ...order, total };

});

```

The totals are 50, 40, and 45. The spread expression preserves the existing order properties while adding `total`. To select paid orders:

```js

const paidOrders = enrichedOrders.filter(order => order.status === 'paid');

```

To calculate paid revenue:

```js

const paidRevenue = paidOrders.reduce((sum, order) => sum + order.total, 0);

// 95

```

This pipeline is readable because each operation has one principal responsibility: `map` enriches records, `filter` selects records, and `reduce` aggregates values. In a production system, prices should be represented according to a defined currency strategy, often as integer minor units such as cents, rather than relying on unrestricted floating-point arithmetic for financial settlement.

10. Applied Case: A Course Dashboard

Background

This illustrative case concerns a university course dashboard displaying a student’s modules, progress, and completion status.

Problem

The dashboard receives JSON containing modules. It must identify incomplete modules, calculate average progress, avoid mutating server data, and display a message when optional profile information is absent.

Analysis

The data may be modeled as an array of objects:

```js

const modules = [

{ code: 'HTML101', title: 'HTML Basics', progress: 100 },

{ code: 'CSS102', title: 'CSS Layout', progress: 72 },

{ code: 'JS103', title: 'JavaScript', progress: 48 }

];

```

Filtering identifies incomplete modules. Mapping extracts progress values, and reducing computes the total. The average is the sum divided by the number of modules; this is valid provided the array is non-empty.

```js

const incomplete = modules.filter(module => module.progress < 100);

const progressTotal = modules.reduce((sum, module) => sum + module.progress, 0);

const average = modules.length === 0 ? 0 : progressTotal / modules.length;

```

The result is an average of 73.33 recurring percent. A display copy can be created without changing the source objects:

```js

const cards = modules.map(({ code, title, progress }) => ({

heading: `${code}: ${title}`,

progressLabel: `${progress}% complete`

}));

```

Optional chaining supports incomplete profile data:

```js

const advisorName = profile?.advisor?.name ?? 'Academic support team';

```

Outcome

The dashboard has a clear data pipeline, preserves source data, handles an empty collection, and provides a meaningful fallback for missing profile information. The same approach can be extended to sorting modules, grouping them by semester, or serializing a filtered view for an API request.

Learning Points

  • Data transformation is easier to test when operations are small and explicit.
  • Destructuring documents which fields a function requires.
  • A default value must distinguish absent data from valid values such as zero.
  • Client-side calculations must not replace server-side authorization or authoritative financial and academic records.

11. Performance, Quality, and Governance

For ordinary user-interface collections, readable array methods are usually adequate. Performance analysis becomes important with large datasets. Repeated `filter`, `map`, and `reduce` operations may traverse the same array multiple times; this can be acceptable for clarity but may require consolidation or server-side processing at scale. Algorithmic cost, memory use, network transfer, and rendering cost should be considered together rather than optimizing prematurely.

Data quality is also a design concern. Applications should define schemas, validate API responses, normalize inconsistent values, and distinguish missing, `null`, and invalid data. TypeScript or runtime validation libraries can complement JavaScript but do not remove the need for sound domain rules.

Security requires special care. Never insert untrusted strings into HTML with `innerHTML` without appropriate sanitization. Do not expose private fields merely by spreading objects into API responses. Validate authorization on the server, because client-side filtering is not access control. Avoid parsing executable content and do not use `eval` for data transformation.

Modern JavaScript continues to evolve through standardized language proposals, improved module systems, and platform APIs. Developers should consult current compatibility data when using newer features, especially in environments with older browsers or constrained runtimes. Automated tests, linting, clear naming, and small pure transformation functions improve maintainability and employability in professional teams.

Chapter Summary

JavaScript represents application data through strings, arrays, objects, and serialized JSON. Arrays support ordered collections and expressive transformations through `map`, `filter`, `find`, `some`, `every`, and `reduce`. Objects provide named properties, while destructuring and spread syntax make extraction and composition concise. Because arrays and objects are reference values, developers must understand mutation, shallow copies, and controlled immutability. Optional chaining and nullish coalescing support safe access and accurate defaults. JSON enables text-based interchange but is not a substitute for validation or authorization. Classes provide encapsulation and reusable behavior, while built-in objects such as `Date`, `Set`, `Map`, and `Math` solve common problems with important limitations. Effective professional code balances readability, correctness, security, performance, and maintainability.

Key Terms

String: An immutable sequence of characters.

Array: An ordered, zero-indexed collection.

Object: A collection of named properties and values.

Reference value: A value whose variable refers to an object stored elsewhere in memory.

Mutation: Modification of an existing object or array.

Shallow copy: A copy whose nested objects remain shared references.

Immutability: A design approach in which transformations create new values instead of changing shared values.

Callback: A function supplied to another function for later execution.

`map`: An array method that creates one transformed output for each input element.

`filter`: An array method that retains elements satisfying a condition.

`reduce`: An array method that combines elements into an accumulated result.

Destructuring: Syntax for extracting values from arrays or object properties.

Spread syntax: Syntax that expands iterable elements or enumerable object properties.

JSON: A text-based data interchange format.

Serialization: Converting a value into a transferable or storable representation.

Class: A language construct for defining object creation and behavior.

Encapsulation: Protecting internal state behind a controlled public interface.

`Set`: A collection of unique values.

`Map`: A collection of key-value pairs with keys of any value type.

Optional chaining: Safe property, element, or method access using `?.`.

Review Questions

Short Answer Questions

1. Why does assigning one object variable to another not create an independent copy?

2. What is the main difference between `map` and `filter`?

3. Why is an initial value recommended when using `reduce`?

4. What problem does optional chaining solve?

5. How do `JSON.stringify` and `JSON.parse` differ?

Long Answer Questions

1. Explain mutation, shallow copying, and immutability using arrays and nested objects.

2. Compare `forEach`, `map`, `filter`, `find`, and `reduce`, including an appropriate use case for each.

3. Explain destructuring, spread syntax, and rest syntax with object and array examples.

4. Discuss the role of classes and encapsulation in a JavaScript application, including situations where composition may be preferable.

5. Explain why JSON validation, authorization, and security remain necessary even when input is valid JSON.

Analytical / Application Questions

1. Design a transformation pipeline that receives an array of products, selects products in stock, applies a discount, sorts them by final price, and returns display-ready objects. Justify each method used.

2. A program uses `const copy = { ...state }`, then changes `copy.user.name`. Analyze why the original state may also change and propose two suitable solutions.

3. A dashboard calculates an average progress value from an array that may be empty and displays an optional advisor name. Write a robust approach using `reduce`, optional chaining, and nullish coalescing, and explain the edge cases.

Multiple Choice Questions with Answers

Question 1

Which array method creates a new array containing one transformed value for each original element?

A. map

B. filter

C. find

D. forEach

Correct Answer: A. map

Question 2

What does `filter` return when no elements satisfy its callback condition?

A. undefined

B. An empty array

C. The original array

D. null

Correct Answer: B. An empty array

Question 3

What is the result of `const result = [1, 2, 3].reduce((sum, n) => sum + n, 0)`?

A. 3

B. 5

C. 6

D. undefined

Correct Answer: C. 6

Question 4

Which syntax safely accesses `user.address.city` when `address` may be undefined?

A. user.address!.city

B. user.address?.city

C. user.address::city

D. user.address??city

Correct Answer: B. user.address?.city

Question 5

What does a later property do in `{ ...base, theme: 'dark' }` when `base` already has a `theme` property?

A. It is ignored

B. It causes a syntax error

C. It deletes the property

D. It overrides the earlier value

Correct Answer: D. It overrides the earlier value

Question 6

Which statement about JSON is correct?

A. JSON supports functions as values

B. JSON.parse converts valid JSON text into a JavaScript value

C. JSON automatically validates business rules

D. JSON can contain comments by standard definition

Correct Answer: B. JSON.parse converts valid JSON text into a JavaScript value

Question 7

Which built-in collection stores only unique values?

A. Array

B. Map

C. Set

D. Date

Correct Answer: C. Set

Question 8

Why is `values.sort()` potentially unsuitable for numeric ascending order?

A. It always reverses the array

B. It compares values as strings by default

C. It cannot sort arrays

D. It returns a number instead of an array

Correct Answer: B. It compares values as strings by default

Question 9

What is a key characteristic of a shallow object copy made with spread syntax?

A. All nested objects are deeply cloned

B. Functions are converted to JSON

C. Nested object references may remain shared

D. The original object is automatically frozen

Correct Answer: C. Nested object references may remain shared

Question 10

Which expression uses a default only when `settings.pageSize` is null or undefined?

A. settings.pageSize || 20

B. settings.pageSize && 20

C. settings.pageSize ?? 20

D. settings.pageSize === 20

Correct Answer: C. settings.pageSize ?? 20

HTML, CSS, JavaScript Basics
Chapter 10 of 15