Array basics
Create JavaScript arrays, read indexes with at() and length, change both ends with push, pop, shift, and unshift, and avoid sparse-array surprises.
- 01Create the array you meanChoose literals, Array(), and Array.of() without falling into the one-number constructor trap.
- 02Read and resize ordered dataUse zero-based indexes, at(), and length confidently, including negative reads and truncation.
- 03Mutate intentionallyPredict push, pop, shift, unshift, extra properties, and holes before they surprise you.
Arrays are ordered lists
An array is JavaScript’s everyday container for an ordered list: a row of values you can keep in sequence, read by position, and pass around as one value. Shopping cart items, search results, breadcrumb labels, recently opened files, and quiz answers are all natural arrays.
An array is a special object that stores values at numbered indexes starting at 0. Its length tracks one more than the highest array index.
Picture a hallway of lockers numbered from left to right. JavaScript starts the labels at 0, so the first locker is index 0, the second is index 1, and the last locker’s number is always one less than length.
- In real life: Locker 0, locker 1, locker 2
- In JavaScript: Array indexes
0,1,2 - In real life: The item inside a locker
- In JavaScript: The element stored at that index
- In real life: The number painted after the last locker
- In JavaScript:
length: last index plus one - In real life: Repainting the number smaller
- In JavaScript: Assigning a smaller
lengthtruncates the array
Where the analogy stops: Real lockers cannot have missing doors between two installed doors without looking strange. JavaScript can: those missing doors are holes, and this lesson shows why they are best avoided.
This lesson builds on Data types, where typeof [] was "object"; Objects & references, where shared references mattered; Object basics; and the Loops, for…of & for…in lesson, which warned you not to use for…in for array values. The Manuals & specifications lesson used .at() as a reference-entry example; now you’ll use it for real.
Array literals and the Array constructor
STEP THROUGHThe usual way to create an array is a literal: square brackets with the values you want. ["Ada", "Grace"] is clear and safe. The Array() constructor exists too, but it has one famous trap: new Array(3) means “length 3”, not “an array containing 3”.
Predict each creation form before stepping. The tricky one is new Array(3): it is not [3].
script
const threeHoles = new Array(3);const twoValues = new Array(1, 2);const oneFromOf = Array.of(3);const withHole = [1, , 3];console.log(oneValue.length, 0 in oneValue);console.log(threeHoles.length, 0 in threeHoles);console.log(twoValues.join("-"));console.log(oneFromOf[0]);console.log(1 in withHole);Notice the difference between a trailing comma and a skipped element. [1, 2,] is just a tolerated trailing comma. [1, , 3] skips the middle element, so index 1 is a hole. The next lesson, Slicing, splicing & joining, returns to Array.of() and Array.from(); for today, remember that literals are the best default.
[3]new Array(3)new Array(1, 2)Array.of(3)[1, , 3][1, undefined, 3]
Sort each expression by whether it creates present array values or holes. Every card explains the rule.
Reading with at() and understanding length
INTERACTIVEIndexes are positions. Bracket notation, arr[0], reads from the start. arr.at(0) does the same, but at() also accepts negative indexes: arr.at(-1) reads the last item. If you ask for a position that does not exist, JavaScript gives undefined instead of throwing.
Change the index, predict each read, then step through. Watch length = 2 shorten the real array.
script
console.log(languages.at(0));console.log(languages.at(-1));console.log(languages[languages.length - 1]);console.log(languages[10]);console.log(languages.at(-10));languages.length = 2;console.log(languages);length is a property, not a method. It usually feels like “how many things are in the list”, but the precise rule is “one more than the highest array index.” You can assign to it: making it smaller deletes elements beyond the new end; making it larger creates holes.
Use arr.at(-1) when you mean “last item”. It reads like your intent and avoids repeating arr.length - 1. Use bracket notation when you already have a non-negative index.
push, pop, shift and unshift
LIVE LABArrays can change. The four first methods to learn all work at the ends of the row:
push(value)adds at the back and returns the new length.pop()removes from the back and returns the removed value.unshift(value)adds at the front and returns the new length.shift()removes from the front and returns the removed value.
The back of an array is like the top of a plate stack: adding and removing there is direct. The front is like the front of a queue: if someone joins or leaves there, everyone behind them may need a new position number.
- In real life: Put a plate on top
- In JavaScript:
push(value)at the back - In real life: Take the top plate
- In JavaScript:
pop()from the back - In real life: Someone joins the front of a line
- In JavaScript:
unshift(value)at the front - In real life: The first person leaves
- In JavaScript:
shift()from the front
Where the analogy stops: Engines optimize common cases, so do not turn this into a stopwatch rule. The intuition is enough: front changes usually make many indexes shuffle, so they are usually slower for very large arrays.
const items = ["Ada", "Grace", "Linus"];items.push(value);items.pop();items.unshift(value);items.shift();items[5] = value;items.length = 2;- returned
- —
- last code
const items = ["Ada", "Grace", "Linus"];
Start with three names. Pick an operation, predict the return value, then watch the indexed boxes change.
The lab also shows two edge cases. Assigning items[5] on a shorter array jumps the length and creates holes in between. Assigning items.length = 2 truncates the row: values after index 1 are removed.
Arrays are objects
STEP THROUGHArrays are special objects. That means they can be shared by reference like any object, they can have ordinary properties, and their keys are property names under the hood. It also means typeof [] is "object", so use Array.isArray() when the distinction matters.
Arrays behave like objects, but they add array-specific rules for indexed elements and length.
script
tools.label = "toolbox";console.log(typeof tools);console.log(Array.isArray(tools));console.log(tools.length);console.log(tools[0] === tools["0"]);console.log(JSON.stringify(Object.keys(tools)));| Indexed element | Ordinary property | |
|---|---|---|
| Example | arr[0] = "Ada" | arr.label = "team" |
| Affects length? | Yes, if it is at or beyond the current end | No |
| Used by array methods? | Yes, methods like map and pop focus on indexes | Usually no |
| Key shape | A string property name that looks like an array index | Any ordinary property key |
This is why for…in is the wrong everyday loop for arrays: it walks enumerable property keys, including surprises from objects. Prefer for…of for values, or array methods in later lessons: Searching arrays, map, filter & reduce, and Sorting & copying safely.
Holes and sparse arrays
STEP THROUGHA hole is a missing indexed property. That is different from a present element whose value is undefined. Both can look similar when printed, but in reveals the difference.
Step slowly: holes are missing properties, not secret undefined values. Most code should avoid creating them.
script
const explicit = [1, undefined, 3];console.log(1 in holey);console.log(1 in explicit);const grown = ["a", "b"];grown[5] = "x";console.log(grown.length, 2 in grown, 5 in grown);console.log(holey.map((value) => value * 2));for (const value of holey) console.log(value);console.log(Array.from({ length: 3 }));| Question | Hole: [1, , 3] | Explicit: [1, undefined, 3] |
|---|---|---|
| Does index 1 exist? | No: 1 in array is false | Yes: 1 in array is true |
| What does reading return? | undefined | undefined |
Do forEach and map visit it? | No, they skip holes | Yes, they visit the element |
| Should you create it on purpose? | Rarely | Sometimes, if undefined is meaningful |
The safest advice is boring and useful: avoid holes in normal app code. If you need three placeholders, use real values such as null, explicit undefined, or objects that say what is missing. The deeper engine-internals lesson Fast arrays & elements kinds will explain why holes can matter for performance too.
Where you’ll use this
Arrays are your first tool when data has order. A real interface may keep a list of notifications, push a new one when it arrives, pop a temporary toast after it closes, or read notifications.at(-1) to find the most recent item. A simple editor can store undo actions with push and reverse the latest action with pop.
- Queues: people waiting for support, messages waiting to send, jobs waiting to run.
- Stacks: undo history, navigation history, open modal dialogs.
- Rows of data: products, table rows, search results, chart points.
- Transform pipelines: later lessons teach
map,filter, andreduce.
Use an array when order and duplicates matter. Use an object or map when named lookup matters more than order. The later Choosing a data structure lesson makes that decision systematic.
Common misconceptions
“new Array(3) is the same as [3].”
No. It creates length 3 with holes. Use [3] or Array.of(3) for one stored value.
“length counts real values.”
Not exactly. length is one more than the highest array index, so holes still stretch it.
“push returns the item I pushed.”
push and unshift return the new length. pop and shift return removed values.
“Holes are just undefined.”
Reading a hole gives undefined, but the index is missing. in tells the difference.
“Arrays are not objects.”
They are special objects. That is why references, properties, and object-key rules still apply.
Practice: array basics
5 EXERCISESPredict what length prints after the three mutations.
const queue = ["Ada", "Grace"];
queue.push("Linus");
queue.unshift("Edsger");
queue.pop();
console.log(queue.length);The final array is ["Edsger", "Ada", "Grace"], so line 5 prints 3.
Use push and pop to model undo. What two lines print?
const undoStack = [];
undoStack.push("typed heading");
undoStack.push("added image");
const lastAction = undoStack.pop();
console.log(lastAction);
console.log(undoStack.length);const undoStack = [];
undoStack.push("typed heading");
undoStack.push("added image");
const lastAction = undoStack.pop();
console.log(lastAction);
console.log(undoStack.length);The most recent action is added image, so pop() returns it. One action remains in the stack.
Read the last score with at(-1). What prints?
const scores = [8, 13, 21];
console.log(scores.at(-1));const scores = [8, 13, 21];
console.log(scores.at(-1));scores.at(-1) reads the last value, 21, without writing scores.length - 1.
A badge list should contain three real placeholder strings, not holes. After the fix, what does the existence check print?
const badges = Array.from({ length: 3 }, () => "new");
console.log(badges.length);
console.log(0 in badges);const badges = Array.from({ length: 3 }, () => "new");
console.log(badges.length);
console.log(0 in badges);The fixed version creates three present entries, so 0 in badges prints true.
Use the in operator to decide whether index 1 exists.
const items = ["first", , "third"];
console.log(1 in items);const items = ["first", , "third"];
console.log(1 in items);The skipped comma creates a hole at index 1, so 1 in items is false.
Quiz: check your understanding
7 QUESTIONSAnswer once from memory, then read every explanation.
Question 1 of 7Which expression creates an array containing one value, the number 3?
Choose an answer to see the explanation.
Question 2 of 7What does at(-1) print here?
Read the code, then predictconst letters = ["a", "b", "c"]; console.log(letters.at(-1));Choose an answer to see the explanation.
Question 3 of 7What do push and length print here?
Read the code, then predictconst list = ["x", "y"]; console.log(list.push("z")); console.log(list.length);Choose an answer to see the explanation.
Question 4 of 7Which method removes and returns the first item of an array?
Choose an answer to see the explanation.
Question 5 of 7What length prints after adding arr.label?
Read the code, then predictconst arr = ["a", "b"]; arr.label = "letters"; console.log(arr.length);Choose an answer to see the explanation.
Question 6 of 7What do the two in-operator checks print?
Read the code, then predictconst holey = [1, , 3]; const explicit = [1, undefined, 3]; console.log(1 in holey); console.log(1 in explicit);Choose an answer to see the explanation.
Question 7 of 7Why are shift and unshift usually slower for very large arrays than push and pop?
Choose an answer to see the explanation.
Key takeaways
- Arrays store ordered values at indexes starting from
0. - Prefer literals.
new Array(3)creates holes;[3]stores one value. at(-1)reads the last item; assigning a smallerlengthtruncates.push/popwork at the back;shift/unshiftwork at the front and usually shuffle more.- Arrays are objects, and holes are missing slots. Avoid sparse arrays unless you have a very specific reason.
Remember the one-liner.
An array is a special object for an ordered, zero-indexed list; keep its values present, and let length tell you where the row ends.
Up next: Slicing, splicing & joining.