
Javascript: Understanding Objects vs Arrays (2 of 2)
In Part 1 of this blog post I omitted one very important detail about the nature of Arrays. You might have heard this already: "Everything is an Object in javascript!". This is technically true. While String, Bool and Number are literals, javascript silently creates a wrapper Object around the primitive at the time of access. There are couple of other special data types that are not Objects (i.e. Undefined and Null), but for the most part you will be dealing with "things" that inherit from the Object prototype. Arrays and Functions also inherit from Object via prototype chain. As the matter of a fact, functions are first class Objects and can be passed as arguments to other functions or even be returned by another function. If you haven't worked with other lambda-like languages, anonymous functions and functions as first class citizens might look weird at a first glance. However, this is a very powerful concept and one of aspects of the language that makes javascript as powerful and fun to work with. But enough about functions, I will leave that topic for another time. What you need to keep in mind is that Array is inheriting from Object. I will explain why this matters in a minute.
This brings us to:
Iteration
Array(s):
If you are coming from languages like Java and PHP, you might want to do something like this:
var myArray = [0, 1, 1, 2, 3, 5, 8, 13, 22]; // for my OCD friends :) var sum = 0; // Don't do this at home! for (var i in myArray) { sum += myArray[i]; } console.log(sum); // prints 55
This resembles for-each loop in PHP/Java and I have seen it used to iterate arrays in javascript way too often. What is even more frustrating is that the example above appears to be working, until it isn't. As mentioned above, Arrays inherit from Object via prototype chain. This becomes important when iterating arrays as we can see in the slightly modified example:
// Augment Object prototype with a new shared property 'foo' Object.prototype.foo = "bar"; var myArray = [0, 1, 1, 2, 3, 5, 8, 13, 22]; // for my OCD friends :) var sum = 0; // Don't do this at home! for (var i in myArray) { sum += myArray[i]; } console.log(sum); // prints 55bar
So what exactly happened here? We never added "bar" to myArray, so why in the world would it show up in sum?
For-in loop in javascript iterates through any enumerable property on the instance and its prototype chain. In this case, for-in loop will iterate through each index in the array, then move onto its prototype (i.e. Object) which has enumerable property 'foo'. There are ways to mitigate this, but instead of fighting our tools we should use standard for loop:
// Augment Object prototype with a new shared property 'foo' Object.prototype.foo = "bar"; var myArray = [0, 1, 1, 2, 3, 5, 8, 13, 22]; // for my OCD friends :) var sum = 0; // Each Array has non-enumerable property 'length' that returns lastIndex + 1 // Note: This doesn't necessarily imply that there are Array.length elements in the array. // For example: // var testArr = []; // testArr[50] = 5; // testArr value is [undefined × 50, 5] and testArr.length will be 51 // This is something to keep in mind as well. for (var i = 0; i < myArray.length; ++i) { sum += myArray[i]; } console.log(sum); // Prints 55 - Woohoo!
Object(s):
For looping through object properties on the other hand, for-in is the way to go.
var parsed = ""; var myObject = { firstname: "Jane", lastname: "Doe", email: "jdoe@email.com" }; for (var prop in myObject) { parsed += prop + ": " + myObject[prop] + "\n"; } // prints // firstname: Jane // lastname: Doe // email: jdoe@email.com console.log(parsed);
It is important to note that above output was in order by accident. Unlike indexed Arrays, Object properties are iterated in arbitrary order and you shouldn't rely on them being in any particular order. Another thing to be aware of is that the loop will go through prototype chain and iterate through any other enumerable properties. I have found this to generally be a desired behavior as for the most part we use prototype chain inheritance to extend and/or decorate current Object. In case where you are interested only in properties for this instance only, you can do additional check within the loop:
Object.prototype.bad = "stuff"; var parsed = ""; var myObject = { firstname: "Jane", lastname: "Doe", email: "jdoe@email.com" }; for (var prop in myObject) { if (!myObject.hasOwnProperty(prop)) { continue; } parsed += prop + ": " + myObject[prop] + "\n"; } // prints // firstname: Jane // lastname: Doe // email: jdoe@email.com console.log(parsed);
To summarize, Arrays are really good at storing ordered lists and ordering things while the cost of removing/splicing elements is a bit higher. Objects are great at representing schemas and the cost of augmenting or deleting properties is very low, but are not good for preserving order. When working with JSON API endpoints that return list of items, you will generally be working with Arrays of Objects.
I hope you find this article helpful and if you have any questions or comments, I would love to hear from you.
Comments
I really enjoyed your article. Very well explained. Now I see how Javascript objects work.
Thank you so much.
Sun, 04/12/2015 - 09:10
I'm happy you found it useful. Cheers!
Tue, 03/10/2015 - 15:56
Thanks so much!
Sun, 04/12/2015 - 09:16
You are welcome!
Mon, 05/11/2015 - 06:41
I thought everything in JS is an object, besides strings, booleans, and a few others... Is an array not an object? Can you be kind enough to give me an indepth resource, i.e. book or very good article. Im not quite on par to understand this stuff but I do know quite a big of javascript.
Thu, 07/23/2015 - 11:04
Can you please tell me, how to add an array inside an object?
As in, it should be something like "{ [ {} ] }".
(Array of objects inside an object)
Also, how can we access the same??
Mon, 11/23/2015 - 14:49
var myObject = {
propOne: 'isString',
arrayInsideObj: [ ]
};
Mon, 11/23/2015 - 23:07
Just to add to this, objects are not enumerated and need to have key value pairs. This means you won't be able to do var x = {[]}; This will throw a syntax error as we are expecting JS to implicitely associate value [] with some property, which it doesn't know how to do. The way we fix this is by being explicit about the property in our object that points to this array: var x = { something: [] };
Hope this helps.
Sat, 08/08/2015 - 16:26
Thanks much for your explanation.
Mon, 11/02/2015 - 10:45
Read your old article just now and want to thank you for all the work you put in Slavko.
Hvala,
Tdude, Stockholm
Mon, 11/23/2015 - 23:03
Nema na cemu - you are welcome! ;)
Tue, 04/12/2016 - 01:34
look forward to read more!
Sun, 04/24/2016 - 02:27
Hi, I was just wondering why you wrote:
for (var prop in myObject) {
if (!myObject.hasOwnProperty(prop)) { continue; }
parsed += prop + ": " + myObject[prop] + "\n";
}
instead of something like:
for (var prop in myObject) {
if (!myObject.hasOwnProperty(prop)) { parsed += prop + ": " + myObject[prop] + "\n";}
}
Wed, 08/10/2016 - 15:24
I'm guessing it was just to make clear the difference between the two examples, by adding an extra line of code rather than altering an existing one. Your suggestion is neater in this case, although there is a typo: you shouldn't have the ! because the sense of the test needs to be reversed.
Sun, 06/05/2016 - 12:16
finally got it too :) thanks !
Tue, 09/06/2016 - 04:47
I really got it. Thanks so much. I am in Canada.
Wed, 09/14/2016 - 19:33
Thank you for clearly explaining these concepts! Adding a new bookmark! :)
Sat, 03/18/2017 - 18:47
Clear and very useful explanation.
Thank you!
Thu, 09/21/2017 - 04:08
Thanks
Wed, 12/31/2014 - 03:40