In-Depth Javascript - Arrays

Definition: Arrays are special objects that store ordered data.
Picture this- you’re organizing a wedding guest list (because let’s face it, in India, that can be a super-hard task!). You have your guests neatly lined up, and you want to keep track of their names. You could store these names in objects, but then you’ll run into a problem — objects don’t care about the order of things. So when Aunt Meera has to be seated before Cousin Rahul, you'll be in trouble.
Enter arrays — the neat little solution to your guest list problems! Arrays are special data structures in JavaScript that allow you to store ordered collections of data. Whether it's names, numbers, or even functions, arrays can handle it all. And, they do so in the order you add them — Aunt Meera always gets her spot first.
Declaring an Array
There are two ways to declare arrays in JavaScript. The most common and easier method looks like this:
let guestList = [];
Alternatively, you can use the new Array()
syntax, but trust me, the square brackets []
are simpler.
Want to pre-fill your guest list? Sure! Here’s how you can add names to the list:
let guestList = ["Aunt Meera", "Cousin Rahul", "Uncle Raj"];
Simple, right? You’ve got your list, neatly organized in one place.
The array guestList
now holds three guests, and they’re all lined up in the same order you added them. Aunt Meera, as always, is in position 0 (arrays start counting from zero).
Accessing Elements:
Want to confirm if Cousin Rahul is coming to the wedding? You can check who’s in spot 1 like this:
console.log(guestList[1]); // Cousin Rahul
Ah, but here’s the catch: array indexing starts from zero. So, the first element is guestList[0]
, the second one is guestList[1], and so on. Get used to it though, in coding an array always start with index 0 and not 1.
Updating the List
Suddenly, Aunt Seema said she would also be coming to the wedding, and you need to add her to the list. You can easily replace an existing guest:
guestList[2] = "Aunt Seema"; // Replaces Uncle Raj with Aunt Seema
Or, you can append her to the end, why remove Uncle Raj, poor Uncle Ji, right?
guestList.push("Aunt Seema");
Now, the guest list is:
["Aunt Meera", "Cousin Rahul", "Uncle Raj", "Aunt Seema"]
Now, let's consider another list of relatives in the array:
const guestList2 = ["Chacha", "Chachi", "Bhaiya", "Bhabhi", "Tauji"];
Here say our dearest Tauji had to leave early, how to remove him from the list easily?
To remove the last item from the array we have a method called as pop
Here is how we use it:
guestList.pop(); // Bye bye Tauji
And poof! Tauji is removed from the guest list. Arrays make these additions and removals super easy just like how wedding coordinators handle last-minute guest changes!
Instead, Want to grab the last item? You can either count or use a shortcut:
console.log(guestList[guestList.length - 1]); // Output: "Tauji"
Here, .length
is like asking “How many items are on my list?” It gives you the total count.
In Some languages index[-1] gives the last index too, but in Javascript that is not the case, In Javascript, to access the last element we can follow the above pattern that we just discussed but it is a bit clumsy right?
That is probably what the developers of Javascript thought too, hence they provided us with the at
method.
console.log(guestList.at(- 1)); // Output: "Tauji"
Modifying Arrays
Let's switch gears now, let' look at some of the array methods that help modify the array.
Let's consider a shopping cart:
let shoppingCart = ["Apples", "Bananas", "Chocolate"];
You can add, remove, and modify items in your array anytime. It’s like upgrading that shopping cart or swapping items around.
Adding Items
Let’s say you forgot to add "Milk" to your shopping cart:
shoppingCart.push("Milk");
console.log(shoppingCart); // Output: ["Apples", "Bananas", "Chocolate", "Milk"]
The .push()
method adds an item to the end of your array. There’s also .unshift()
, which works like squeezing something at the beginning of your cart:
shoppingCart.unshift("Eggs");
console.log(shoppingCart); // Output: ["Eggs", "Apples", "Bananas", "Chocolate", "Milk"]
Removing Items
Oops, "Bananas" went bad? Let’s remove them:
shoppingCart.splice(2, 1); // Removes 1 item starting from index 2
console.log(shoppingCart); // Output: ["Eggs", "Apples", "Chocolate", "Milk"]
Splice can both remove and replace items in one go! It is something you will find yourself using a lot during development.
Internal working of the array
1. Arrays vs. Objects
In JavaScript, arrays are essentially specialized objects. Each element in the array is stored as a property of the array object, where the indices (0, 1, 2, ...) are the property keys. Here's an example:
let arr = [10, 20, 30];
Internally, JavaScript interprets this array as:
{
0: 10,
1: 20,
2: 30,
length: 3
}
The keys are the indices (0
, 1
, 2
), and the values are the corresponding elements of the array (10
, 20
, 30
). The array also has a length
property, which is updated automatically.
2. Arrays are Dynamic
Unlike some other programming languages (like C++ or Java), JavaScript arrays are dynamic. This means their size can change during runtime. You can add and remove elements without needing to manually allocate or deallocate memory. JavaScript manages this for you.
For example, when you use .push()
or .splice()
, JavaScript reallocates memory behind the scenes to accommodate changes in the array size.
let arr = [1, 2, 3];
arr.push(4); // Adds an element to the end
console.log(arr); // [1, 2, 3, 4]
Internally, the JavaScript engine will resize the memory block to make room for the new element.
3. Sparse Arrays
JavaScript arrays don’t require continuous memory allocation. This allows them to be sparse, meaning some indices can be left unfilled, creating "holes" in the array.
let arr = [];
arr[100] = 'Hello';
console.log(arr.length); // 101
In this case, the array arr
has a length of 101, but only one element at index 100
. The rest of the indices (0
to 99
) are considered empty or undefined.
This is different from languages with strict array implementations, where the array needs to have continuous values and memory locations.
4. Optimizations by JavaScript Engines
JavaScript engines (like Google’s V8, used in Chrome and Node.js) apply various optimizations to arrays based on how they are used:
- Packed vs. Sparse Arrays: Arrays with contiguous indices are stored as packed arrays, which means the engine can optimize access and memory usage by treating them similarly to arrays in lower-level languages. If the array becomes sparse (i.e., non-contiguous indices), the engine switches to a sparse array representation, which uses a hash map internally. This is slower than packed arrays but more memory-efficient for sparse data.
- Elements Kind: JavaScript engines also optimize based on the type of data in the array. If all elements are numbers, the engine can treat the array as a list of numbers. If a non-number is added later, it has to change its internal structure to accommodate mixed types.
For example:
let arr = [1, 2, 3]; // Initially treated as a list of numbers
arr.push("Hello"); // Engine has to switch to a mixed-type array
5. Memory Management
JavaScript uses a garbage collector to manage memory, including for arrays. When an array is no longer in use, the garbage collector automatically frees up the memory used by the array. You don’t need to manually manage memory like in languages such as C or C++.
However, you can still run into memory-related performance issues if your array grows excessively large, as the garbage collector might take time to clean up unused memory. More about the Garbage collector is discussed in a separate article on the same.
6. Indexing and Accessing Elements
Accessing array elements by index is generally fast in JavaScript, particularly for packed arrays. The engine can directly access the memory location corresponding to the index, similar to how arrays work in lower-level languages.
For sparse arrays, however, accessing an element may require more time, as the engine has to look up the element in a hash map rather than directly accessing it from contiguous memory.
Deep Javascript internals - Optional reading
How it Works in V8 (Chrome's JavaScript Engine)
let numbers = [1, 2, 3, 4];
When you create this array:
- Array Creation: Initially, the V8 engine creates a packed array of numbers, allocating a contiguous block of memory to store the elements
[1, 2, 3, 4]
. - Accessing Elements: When you access an element like
numbers[2]
, the engine can quickly compute the memory location using the base address of the array and the index. This gives a time complexity of O(1). - Adding an Element: If you add an element using
numbers.push(5)
, the engine checks if there is enough allocated space. If there is, it adds5
to the end. If not, it reallocates more memory to accommodate the new element. - Switching to Sparse: If you suddenly decide to add an element at a distant index like
numbers[1000] = 999
, V8 changes the internal representation from a packed array to a sparse one, using a hash map to store non-contiguous indices.
More about how the Javascript engine works is discussed in a separate article.
With this great understanding of arrays now, let's understand about Array methods in the next one.