📦 Lesson 9: Arrays
Variables hold a single value — but what if you need a list of values? Arrays let you store, access, and manipulate ordered collections of data.
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Create arrays using literal syntax and the
Arrayconstructor - Access and update elements by index
- Use
lengthto find and control the size of an array - Add and remove elements with
push,pop,unshift, andshift - Insert, remove, or replace elements with
splice - Search arrays with
includes,indexOf, andfind - Combine and extract arrays with
concat,slice, and the spread operator - Iterate over arrays with
for,for...of, andforEach
Estimated Time: 50 minutes
Project: Build a to-do list manager using array operations
📑 In This Lesson
Introduction
So far, every variable you've created holds a single value — one number, one string, one boolean. But real programs almost always work with collections of data: a list of usernames, a set of quiz scores, a shopping cart full of items.
That's what arrays are for. An array is an ordered list of values, and it's the most commonly used data structure in JavaScript. You'll use arrays in virtually every project you build.
Each value in an array is called an element, and each element has a numbered position called its index. Indexes start at 0, not 1 — this is true across almost all programming languages.
Creating Arrays
The most common way to create an array is with square bracket notation (an array literal). You can also create empty arrays and fill them later.
// Array literal — the standard way
const fruits = ["apple", "banana", "cherry"];
// Empty array — start with nothing, add later
const scores = [];
// Arrays can hold any type — and mixed types
const mixed = [42, "hello", true, null, undefined];
// Nested arrays (arrays inside arrays)
const grid = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
⚠️ Avoid the Array Constructor
You can create arrays with new Array(), but it has a confusing quirk: new Array(3) creates an empty array with 3 slots, not an array containing the number 3. Stick with square brackets.
// Confusing constructor behavior
const a = new Array(3); // [ , , ] — 3 empty slots!
const b = new Array("3"); // ["3"] — one element
// ✅ Just use square brackets
const c = [3]; // [3] — one element, no confusion
const with Arrays
Declaring an array with const prevents you from reassigning the variable, but you can still modify the contents. The const protects the reference, not the data inside.
const colors = ["red", "green", "blue"];
// ✅ Modifying contents is allowed
colors.push("yellow"); // ["red", "green", "blue", "yellow"]
colors[0] = "crimson"; // ["crimson", "green", "blue", "yellow"]
// ❌ Reassigning the variable is NOT allowed
// colors = ["purple"]; // TypeError: Assignment to constant variable
Accessing & Modifying Elements
Use square brackets with an index number to read or change any element. Remember: the first index is 0.
const animals = ["cat", "dog", "bird", "fish"];
// Reading elements
console.log(animals[0]); // "cat" (first element)
console.log(animals[2]); // "bird" (third element)
console.log(animals[3]); // "fish" (last element in this array)
// Updating elements
animals[1] = "wolf";
console.log(animals); // ["cat", "wolf", "bird", "fish"]
// Accessing out of bounds — no error, just undefined
console.log(animals[99]); // undefined
Accessing the Last Element
To get the last element, you can use length - 1 or the modern at() method with a negative index.
const letters = ["a", "b", "c", "d", "e"];
// Classic approach
console.log(letters[letters.length - 1]); // "e"
// Modern approach — at() supports negative indexes
console.log(letters.at(-1)); // "e" (last)
console.log(letters.at(-2)); // "d" (second to last)
console.log(letters.at(0)); // "a" (same as letters[0])
Destructuring Arrays
You can unpack array values directly into variables with destructuring.
const rgb = [255, 128, 0];
// Without destructuring
const red = rgb[0];
const green = rgb[1];
const blue = rgb[2];
// With destructuring — cleaner!
const [r, g, b] = rgb;
console.log(r); // 255
console.log(g); // 128
console.log(b); // 0
// Skip elements with commas
const [first, , third] = ["a", "b", "c"];
console.log(first); // "a"
console.log(third); // "c"
The length Property
Every array has a length property that tells you how many elements it contains. It updates automatically as you add or remove elements.
const items = ["pen", "paper", "eraser"];
console.log(items.length); // 3
items.push("ruler");
console.log(items.length); // 4
// Common pattern: check if array is empty
if (items.length === 0) {
console.log("No items!");
} else {
console.log(`You have ${items.length} items.`);
}
💡 length Is Not Read-Only
You can set length to truncate an array or extend it (with empty slots). This is rarely useful but good to know.
const nums = [1, 2, 3, 4, 5];
nums.length = 3;
console.log(nums); // [1, 2, 3] — last two elements removed!
nums.length = 5;
console.log(nums); // [1, 2, 3, empty × 2] — extended with empty slots
Adding & Removing Elements
JavaScript provides four core methods to add or remove elements from the beginning or end of an array.
push() and pop() — End of Array
const stack = ["a", "b", "c"];
// push — add one or more elements to the END
stack.push("d");
console.log(stack); // ["a", "b", "c", "d"]
stack.push("e", "f");
console.log(stack); // ["a", "b", "c", "d", "e", "f"]
// push returns the NEW length
const newLength = stack.push("g");
console.log(newLength); // 7
// pop — remove the LAST element
const removed = stack.pop();
console.log(removed); // "g"
console.log(stack); // ["a", "b", "c", "d", "e", "f"]
unshift() and shift() — Beginning of Array
const queue = ["b", "c", "d"];
// unshift — add to the BEGINNING
queue.unshift("a");
console.log(queue); // ["a", "b", "c", "d"]
// shift — remove from the BEGINNING
const first = queue.shift();
console.log(first); // "a"
console.log(queue); // ["b", "c", "d"]
💡 Performance Note
push/pop are fast — they work on the end of the array without moving other elements. unshift/shift are slower because every other element needs to shift position. For small arrays this doesn't matter, but keep it in mind for large datasets.
| Method | Position | Action | Returns | Modifies Original? |
|---|---|---|---|---|
push() | End | Add | New length | Yes |
pop() | End | Remove | Removed element | Yes |
unshift() | Start | Add | New length | Yes |
shift() | Start | Remove | Removed element | Yes |
Splice — The Swiss Army Knife
The splice() method can remove, insert, or replace elements at any position in an array. It's the most versatile array method.
// Syntax: array.splice(startIndex, deleteCount, ...newItems)
const colors = ["red", "green", "blue", "yellow", "purple"];
Removing Elements
const colors = ["red", "green", "blue", "yellow", "purple"];
// Remove 2 elements starting at index 1
const removed = colors.splice(1, 2);
console.log(removed); // ["green", "blue"]
console.log(colors); // ["red", "yellow", "purple"]
Inserting Elements
const colors = ["red", "yellow", "purple"];
// Insert at index 1, delete 0 elements
colors.splice(1, 0, "orange", "green");
console.log(colors); // ["red", "orange", "green", "yellow", "purple"]
Replacing Elements
const colors = ["red", "orange", "green", "yellow", "purple"];
// At index 2, remove 1 element and insert "blue"
colors.splice(2, 1, "blue");
console.log(colors); // ["red", "orange", "blue", "yellow", "purple"]
// Replace multiple elements
colors.splice(0, 2, "crimson");
console.log(colors); // ["crimson", "blue", "yellow", "purple"]
splice(1, 2)"] A --> C["Insert only
splice(1, 0, 'new')"] A --> D["Replace
splice(1, 1, 'new')"] style A fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style B fill:#fef2f2,stroke:#ef4444,stroke-width:2px,color:#1e293b style C fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b style D fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b
⚠️ splice vs. slice
Don't confuse splice and slice! They sound similar but behave very differently. splice modifies the original array. slice (covered below) copies a portion without changing the original.
Searching Arrays
JavaScript gives you several ways to check if an array contains a value or find where it is.
includes() — Does It Exist?
const fruits = ["apple", "banana", "cherry"];
console.log(fruits.includes("banana")); // true
console.log(fruits.includes("grape")); // false
// Common pattern: conditional check
if (fruits.includes("cherry")) {
console.log("Cherry is in stock!");
}
indexOf() — Where Is It?
const fruits = ["apple", "banana", "cherry", "banana"];
console.log(fruits.indexOf("banana")); // 1 (first occurrence)
console.log(fruits.indexOf("grape")); // -1 (not found)
// Classic pattern: check existence before indexOf existed
if (fruits.indexOf("cherry") !== -1) {
console.log("Found cherry!");
}
find() — Get the First Match
find() takes a function and returns the first element that makes the function return true. This is especially useful with arrays of objects (which you'll learn about next lesson).
const numbers = [3, 7, 12, 5, 22, 8];
// Find the first number greater than 10
const big = numbers.find(num => num > 10);
console.log(big); // 12
// Find the first even number
const even = numbers.find(num => num % 2 === 0);
console.log(even); // 12
// If nothing matches, returns undefined
const huge = numbers.find(num => num > 100);
console.log(huge); // undefined
findIndex() — Where Is the First Match?
const numbers = [3, 7, 12, 5, 22, 8];
const index = numbers.findIndex(num => num > 10);
console.log(index); // 2 (the index of 12)
const notFound = numbers.findIndex(num => num > 100);
console.log(notFound); // -1
| Method | Returns | Best For |
|---|---|---|
includes(value) | true / false | Simple existence check |
indexOf(value) | Index or -1 | Finding position of exact value |
find(fn) | Element or undefined | Finding first match by condition |
findIndex(fn) | Index or -1 | Finding position of first match |
Combining & Extracting
concat() — Merge Arrays
concat() creates a new array by joining two or more arrays together. The originals are unchanged.
const front = ["a", "b"];
const back = ["c", "d"];
const extra = ["e"];
const combined = front.concat(back);
console.log(combined); // ["a", "b", "c", "d"]
console.log(front); // ["a", "b"] — unchanged!
const all = front.concat(back, extra);
console.log(all); // ["a", "b", "c", "d", "e"]
Spread Operator — The Modern Way
The spread operator (...) "spreads" an array's elements into a new array. It's the preferred modern approach to combining arrays.
const front = ["a", "b"];
const back = ["c", "d"];
// Spread into a new array
const combined = [...front, ...back];
console.log(combined); // ["a", "b", "c", "d"]
// Add extra elements in between
const withMiddle = [...front, "X", "Y", ...back];
console.log(withMiddle); // ["a", "b", "X", "Y", "c", "d"]
// Copy an array (shallow copy)
const original = [1, 2, 3];
const copy = [...original];
copy.push(4);
console.log(original); // [1, 2, 3] — unaffected
console.log(copy); // [1, 2, 3, 4]
slice() — Extract a Portion
slice() returns a new array containing a portion of the original. It does not modify the original. Think of it as copying a "slice" out of the array.
const letters = ["a", "b", "c", "d", "e"];
// slice(startIndex, endIndex) — end is NOT included
console.log(letters.slice(1, 3)); // ["b", "c"]
console.log(letters.slice(2)); // ["c", "d", "e"] (from index 2 to end)
console.log(letters.slice(-2)); // ["d", "e"] (last 2 elements)
console.log(letters.slice()); // ["a", "b", "c", "d", "e"] (full copy)
// Original is unchanged
console.log(letters); // ["a", "b", "c", "d", "e"]
✅ Mutating vs. Non-Mutating Methods
Some methods change the original array (mutating), while others return a new one and leave the original alone (non-mutating). Knowing the difference prevents bugs!
- Mutating:
push,pop,shift,unshift,splice,sort,reverse - Non-mutating:
concat,slice,map,filter,find,includes
Iterating Over Arrays
You'll often need to do something with every element in an array. Here are the three main approaches.
Classic for Loop
Gives you full control — you get the index and can break early, skip iterations, or go backwards.
const scores = [85, 92, 78, 95, 88];
for (let i = 0; i < scores.length; i++) {
console.log(`Score ${i + 1}: ${scores[i]}`);
}
// Score 1: 85
// Score 2: 92
// ...
for...of Loop
Cleaner when you just need the values and don't care about indexes.
const fruits = ["apple", "banana", "cherry"];
for (const fruit of fruits) {
console.log(fruit.toUpperCase());
}
// APPLE
// BANANA
// CHERRY
forEach() Method
forEach calls a function for each element. It gives you both the element and its index.
const colors = ["red", "green", "blue"];
colors.forEach((color, index) => {
console.log(`${index}: ${color}`);
});
// 0: red
// 1: green
// 2: blue
💡 Which Loop Should I Use?
for...of— default choice for most situations. Clean and readable.for— when you need the index, or needbreak/continue.forEach— when you want a callback pattern. Note: you can'tbreakout offorEach.
Building New Arrays from Loops
const prices = [10, 20, 30, 40];
// Add tax to each price
const withTax = [];
for (const price of prices) {
withTax.push(price * 1.08);
}
console.log(withTax); // [10.8, 21.6, 32.4, 43.2]
// In the next lesson (Array Methods), you'll learn
// a cleaner way: prices.map(price => price * 1.08)
Hands-on Exercise
🏋️ Exercise: To-Do List Manager
Objective: Build a set of functions that manage a to-do list using array methods.
// Start with this to-do list
const todos = ["Buy groceries", "Walk the dog", "Finish homework"];
// TODO: Write these functions:
// 1. addTodo(list, task)
// - Add a task to the end of the list
// - Return the updated list
// 2. removeTodo(list, task)
// - Find and remove the task from the list
// - If the task doesn't exist, log "Task not found: [task]"
// - Return the updated list
// 3. insertTodoAt(list, index, task)
// - Insert a task at a specific position
// - Return the updated list
// 4. listTodos(list)
// - Log each task with a number: "1. Buy groceries"
// - If the list is empty, log "No tasks — you're all caught up!"
// 5. completeTodo(list)
// - Remove and return the FIRST task (treat the list as a queue)
// - Log "Completed: [task]"
// - If list is empty, log "Nothing to complete!"
// Test your functions:
// addTodo(todos, "Read a book");
// listTodos(todos);
// removeTodo(todos, "Walk the dog");
// completeTodo(todos);
// insertTodoAt(todos, 1, "Call dentist");
// listTodos(todos);
💡 Hint
addTodo: Use push().
removeTodo: Use indexOf() to find the task's position, then splice() to remove it. Check for -1 first (not found).
insertTodoAt: Use splice(index, 0, task) to insert without removing.
listTodos: Use forEach and check length === 0 first.
completeTodo: Use shift() to remove and return the first element.
✅ Solution
const todos = ["Buy groceries", "Walk the dog", "Finish homework"];
function addTodo(list, task) {
list.push(task);
return list;
}
function removeTodo(list, task) {
const index = list.indexOf(task);
if (index === -1) {
console.log(`Task not found: ${task}`);
} else {
list.splice(index, 1);
}
return list;
}
function insertTodoAt(list, index, task) {
list.splice(index, 0, task);
return list;
}
function listTodos(list) {
if (list.length === 0) {
console.log("No tasks — you're all caught up!");
return;
}
list.forEach((task, i) => {
console.log(`${i + 1}. ${task}`);
});
}
function completeTodo(list) {
if (list.length === 0) {
console.log("Nothing to complete!");
return;
}
const done = list.shift();
console.log(`Completed: ${done}`);
return done;
}
// Testing
addTodo(todos, "Read a book");
listTodos(todos);
// 1. Buy groceries
// 2. Walk the dog
// 3. Finish homework
// 4. Read a book
removeTodo(todos, "Walk the dog");
listTodos(todos);
// 1. Buy groceries
// 2. Finish homework
// 3. Read a book
completeTodo(todos);
// Completed: Buy groceries
insertTodoAt(todos, 1, "Call dentist");
listTodos(todos);
// 1. Finish homework
// 2. Call dentist
// 3. Read a book
🎯 Quick Quiz
Question 1: What is the index of the first element in a JavaScript array?
Question 2: Which method adds an element to the END of an array?
Question 3: What does splice() do?
Question 4: What does ["a","b","c"].includes("b") return?
Summary
🎉 Key Takeaways
- Arrays store ordered collections of values in a single variable
- Indexes start at 0 — the first element is
array[0] push/popadd/remove from the end;unshift/shiftadd/remove from the startsplice()can insert, remove, or replace elements at any positionincludeschecks existence;indexOffinds position;findfinds by conditionsliceand the spread operator create copies without modifying the originalfor...ofis the cleanest way to loop through arraysconstarrays can have their contents changed —constonly locks the variable binding
📚 Additional Resources
🚀 What's Next?
Now that you can create and manipulate arrays, the next lesson covers objects — JavaScript's other essential data structure. Objects let you store data with named keys instead of numbered indexes, which is perfect for representing real-world things like users, products, and settings.