Mastering Arrays in JavaScript: From Basics to Interview-Ready Exercises
If you’ve ever stored a list of items—like favorite movies, user emails, or even a to-do list—you’ve already used an array. It’s one of the simplest yet most powerful data structures. Arrays organize elements sequentially in memory, making it super fast to access any element using its index.
In JavaScript, arrays are dynamic, meaning you can keep adding or removing elements without worrying about memory management. But don’t be fooled by their simplicity—understanding arrays deeply gives you a massive edge in interviews and in real-world coding.
How Arrays Actually Work
Behind the scenes, arrays are stored in contiguous memory blocks. That’s why computers can find any element instantly using its index. This lookup operation runs in O(1)—constant time.
But not all operations are created equal.
Adding or removing from the end of an array (push and pop) is lightning-fast. However, inserting or deleting from the start (unshift or splice) is slower—O(n)—because every other item has to move one step.
Here’s a quick summary:
| Operation | Time Complexity | Description |
|---|---|---|
| Access (get) | O(1) | Direct index lookup |
| Push / Pop | O(1) | Add or remove from end |
| Unshift / Splice | O(n) | Insert/delete in middle or start |
That trade-off explains why other data structures (like linked lists) exist—they’re optimized for cases where insertions and deletions matter more than random access.
Static vs. Dynamic Arrays—The Memory Game
In low-level languages like C or C++, arrays are static. You have to declare their size upfront (int numbers[10]). Need to add an 11th element? Tough luck—you’ll have to create a new array and copy everything over.
JavaScript, Python, and Java use dynamic arrays. They grow automatically when full: the engine allocates a bigger chunk of memory (often twice the size), copies existing data, and adds your new element. Most of the time, push() runs in O(1), but during that occasional resize, it jumps to O(n).
So next time you push() without thinking, remember—your array might be silently doing a lot of work for you.
Building Your Own Array (Yes, Really)
To truly grasp arrays, nothing beats building one from scratch. Let’s create our own version—MyArray—using a JavaScript class.
class MyArray {
constructor() {
this.length = 0;
this.data = {};
}
get(index) {
return this.data[index];
}
push(item) {
this.data[this.length] = item;
this.length++;
return this.length;
}
pop() {
const lastItem = this.data[this.length - 1];
delete this.data[this.length - 1];
this.length--;
return lastItem;
}
delete(index) {
const item = this.data[index];
this.shiftItems(index);
return item;
}
shiftItems(index) {
for (let i = index; i < this.length - 1; i++) {
this.data[i] = this.data[i + 1];
}
delete this.data[this.length - 1];
this.length--;
}
}
This mini array supports get, push, pop, and delete. It mirrors how JavaScript arrays actually behave under the hood—constant-time access and amortized constant-time inserts at the end, but slower deletions in the middle.
If you understand this, you already understand dynamic arrays at their core.
Classes, References, and Context in JavaScript
While we’re at it, let’s clear up a few concepts that often confuse developers when working with objects and arrays.
Arrays are reference types, meaning that if you assign one array to another variable, they both point to the same place in memory. Mutate one, and you mutate both.
const a = [1, 2];
const b = a;
b.push(3);
console.log(a); // [1, 2, 3]
Also, understanding this inside classes helps prevent weird bugs. In JavaScript, this refers to who called the function, not necessarily where it’s defined. Arrow functions, however, inherit this from their surrounding scope—a subtle but crucial distinction when designing reusable data structures or classes.
Exercise 1: Reverse a String
A classic warm-up problem that’s deceptively simple.
Prompt:
Reverse a string like this: “Hi My Name is Alex” → “xelA si emaN yM iH”
Approach 1 — Manual Loop
function reverse(str) {
if (!str || typeof str !== 'string' || str.length < 2) return str;
const backwards = [];
for (let i = str.length - 1; i >= 0; i--) {
backwards.push(str[i]);
}
return backwards.join('');
}
Approach 2 — Built-in Methods
function reverse(str) {
return str.split('').reverse().join('');
}
Approach 3 — ES6 One-liner
const reverse = str => [...str].reverse().join('');
All three have the same O(n) time complexity—but showcasing multiple solutions in interviews shows flexibility and depth.
Exercise 2: Merge Two Sorted Arrays
Another staple interview problem: merging two sorted arrays into one sorted array.
Example: [0,3,4,31] and [4,6,30] → [0,3,4,4,6,30,31]
Solution:
function mergeSortedArrays(arr1, arr2) {
if (arr1.length === 0) return arr2;
if (arr2.length === 0) return arr1;
const merged = [];
let i = 0, j = 0;
while (i < arr1.length && j < arr2.length) {
if (arr1[i] < arr2[j]) {
merged.push(arr1[i]);
i++;
} else {
merged.push(arr2[j]);
j++;
}
}
return merged.concat(arr1.slice(i)).concat(arr2.slice(j));
}
This algorithm runs in O(n + m) time and is an excellent example of pointer logic, a foundational technique for many more advanced problems (like merging linked lists or sorted intervals).
Wrapping Up—Why Arrays Still Matter
Arrays might feel basic, but mastering them means mastering memory, complexity, and logic. They’re a microcosm of how computers think—linear, efficient, and structured.
Every time you push(), splice(), or even write a for loop, you’re working with the same principles that power modern algorithms. And as you move on to more complex data structures—linked lists, stacks, trees—you’ll keep finding that arrays are the foundation of it all.
Final Thought:
If you can implement, explain, and optimize arrays confidently, you’ve already crossed the first big hurdle of algorithmic thinking. Everything else builds from here.