A valiant array exists both as an Array as well as an Object. That can't be good, right? Read on to find out!
As we have seen so far, arrays are all about making it super simple to work with collections of data. This simplicity is partly through the various properties and methods available to us. It is also through the unique ways we have for accessing the data stored by our arrays using the bracket notation. Despite all the awesome things arrays have going for them, since we are still living inside a world whose boundaries are defined by JavaScript, our arrays are still just an extension of the humble Object.
This detail is important because it can help shed light on why our arrays behave the way they do when we use them in ways they both were and weren't designed for. We will dive deeper into all of this in the following sections.
Onwards!
To kick your array skills into the stratosphere, everything you need to be an arrays expert is available in this book.
BUY ON AMAZONIf we look at the prototype chain, an Array is just two degrees (using a Kevin Bacon-ism) removed from a plain old Object where we go from Array, to Array.prototype, to Object.prototype. The following diagram shows what this prototype chain looks like for an array called myItems storing the numbers 1, 2, and 3:
The main takeaway from seeing the prototype chain is this: our arrays provide a lot of capabilities, but if we deviate too far from their supported path, our array object will behave like a typical object...and all the opportunities and pitfalls that come from that.
This is most evident when it comes to using the bracket notation to access items in our array. Each array item has an index position/value, and this is represented as an integer that starts at 0 with the first item and goes up until we reach the last item at the end of our array:
This makes accessing array values straightforward where we use a bracket notation and specify the index position of the item we are interested in:
let greetings = ["hi", "sup", "hello", "yo", "hey"];
console.log(greetings[3]); // "yo"
Here is where things get a little muddy. Using this bracket notation to retrieve things isn't unique to arrays. It is a core part of how we can access values in regular objects as well:
let foo = { a: "hello",
b: "good bye!"
};
foo["c"] = "blah!";
console.log(foo["c"]); // "blah!";
The biggest difference in the bracket notation between arrays and objects has to do with what the key values can be. The key values we can specify for objects can be a lot of valid identifiers - numbers, text, and more. For arrays, the key has to be a positive integer value if we want the data we are dealing with to be treated like an array. This bolded part is important, because the following is totally valid:
let myArray = ["one", "two", "three"];
myArray["foo"] = "What's up?";
console.log(myArray[0]); // "one"
console.log(myArray["foo"]); // "What's up?"
If we visualize what the items stored bymyArray look like, this is what we will see:
At least, that is what we might expect to see given how effortlessly we were able to access the values stored in myArray using the key values of 0 and foo. What we actually have is a region of items that are a part of the Array world and a region that is part of the Object world:
The Array and Object regions are quite independent. All of the array operations only operate within this Array region. For example, checking the length of myArray will return 3. The length calculation won't cross into the Object region (aka the array's object property collection) and include the item stored at foo. Similarly, myArray.indexOf("what's up?") will return a -1 indicating it wasn't able to find "what's up?" as a value stored by our array. To say that arrays are territorial is an understatement!
When iterating over our arrays, especially if they happen to have both Array items and Object items as shown with myArray earlier, the behavior you will see when using for...of and for...in will vary. Take a look at the following code:
let myArray = ["one", "two", "three"];
myArray["foo"] = "What's up?";
// using for...of
for (const value of myArray) {
console.log(value); // "one", "two", "three"
}
// using for...in
for (const key in myArray) {
console.log(myArray[key]); // "one", "two", "three", "What's up?"
}
With for...of, what we will iterate through are just the values stored by our array. We won't be jumping into the Object region and accessing any values stored by our array's object property collection.
Things are different with for...in. What the for...in loop returns are all enumerable properties on an object. This means that what we will see are properties for both Arrays as well as properties on any prototypes like Object. If you rely on iterating through collections of data using for...in or for...of, these details may be important.
In JavaScript, everything inherits from Object. This is one of those truths that never quite hits our radar except when something isn't working right. Those somethings often crop up when we are working with types like Arrays where their behavior overlaps with Objects in some strange ways. Over the past many paragraphs, we looked at how Arrays and Objects allow us to store and retrieve data, but the similarities end very quickly where arrays live in their own world and objects live in theirs. The tricky part is that an array can live as both an array and object, so...yeah!
Just a final word before we wrap up. If you have a question and/or want to be part of a friendly, collaborative community of over 220k other developers like yourself, post on the forums for a quick response!
:: Copyright KIRUPA 2024 //--