1
votes

Hi I am attaching a screenshot of my Firefox Console and would like to know why does array behave like this in javascript.

enter image description here

I was trying to understand for...in and for...of in js but came across this problem. I now understand that for...in is printing just the key while for...of is printing the value part. But i don't understand why did array a take key value pair in the first place?

I suppose as Array is also an Object it can take A key:value pair as input but then when i console.log(a) then why does it not print foo: bar, even though printing a["foo" ] prints "bar". Is there some concept that i'm missing out and unable to understand?

However Chrome browser displays as follow:

enter image description here

In this case printing array a is giving me [1,2,3,foo: "bar"] as output however if i define

var b=[1,2,3,foo: "bar"] 

it gives me unexpected token . Why is it so?

2
Becuse you have created an "array like object". Arrays have numeric indexing and when you add a['foo'] it becomes a hybrid. Not a good thing to do if you don't know what you are doing with an "array like object"charlietfl
for...in iterates over enumerable properties while for...of uses Array's @@iterator. The output of console.log is simply a choice of the browser/environment. Node.js and Chrome DevTools will print [ 1, 2, 3, foo: 'bar' ], for example.vox
@shinite you can add properties to an array after it has been constructed but before hand it has to be valid syntaxcharlietfl
@shinite console.log does not necessarily print out valid JavaScript. It is for display purposes only. console.log( Object.assign( {}, a ) ); would give you a valid object, however. Note it wouldn't be an Array.vox
There foo:bar in your printed array will most likely have a drop down arrow to expand and see the object (after expanding the array in the console). But to add an object to an array, you can't just do foo:bar. Objects need to be wrapped in {}Tyler Dahle

2 Answers

1
votes

Here is a good question :-) Roughly speaking, an array is an object with a length property, a set of properties labeled from 0 to length - 1, and a couple of specific features. We could almost say that a = [1, 2, 3] is a shortcut for :

a = { 0: 1, 1: 2, 2: 3, length: 3 }

In fact, you can easily simulate an array using a plain object.

An object is a collection of key-value pairs, like a dictionary. A key can be any arbitrary string, and a value can be any type of data. In JavaScript, almost everything is an object. An array is an object, a function is an object, even atomic data like numbers and strings can be manipulated like an object, indeed, JavaScript secretly creates an object in order to wrap the atomic data.

There are a couple of ways to create an object. Often you'll see either the literal notation based on brackets ({} and []) or the new keyword. The literal notation is just syntactic sugar, in fact, your computer executes the exact same sequence of instructions in both cases. In other words, o = {} is the same as o = new Object(), and a = [] is the same as a = new Array().

Yes, when you write a = [], you are implicitely creating an instance of the Array class, this is what makes an array a special object :-) As stated above, an array is an object with additional features, and these features are inherited from the Array class. The indexOf method is one of them (type Array.prototype in a console to list them all) :

var a = ["a", "b", "c"];
a.indexOf("b"); // 1

Until recently, as long as you didn't call Array specific methods from the object itself, you could easily cheat on JavaScript by adding a length property to a plain object and use it as an array. JQuery takes advantage of this technique : http://api.jquery.com/Types/#ArrayLikeObject.

var o = { length: 0 };
Array.prototype.push.call(o, "a", "b");
console.log(o, Array.prototype.slice.call(o));

Today this is a little harder to achieve, for example, you have to implement an iterator to be able to use a plain object in a for ... of loop :

var o = {
  0: "a", 
  1: "b", 
  2: "c", 
  length: 3 
};

// o[Symbol.iterator] = Array.prototype[Symbol.iterator];
// works as well, but I want you to see there is no magic :-)

o[Symbol.iterator] = function() {
  return {
    i: 0,
    o: this,
    next: function() {
      var o = this.o;
      var i = this.i++;
      return {
        value: o[i],
        done: i === o.length
      }
    }
  };
};

for (var x of o) {
  console.log(x);
}

for (var x in o) {
  console.log(x, o[x]);
}

Note that JavaScript ignores the length property of a true array in a for ... in loop. In my humble opinion this is a questionable choice that is most likely to cause a blue screen in the brain of the candid newbie...

var a = [1, 2, 3];
a["i'm"] = "your father";

console.log("length", a.length);
for (var k in a) {
  console.log(k, a[k]);
}

Final word, don't get confused by the output of the Chrome's console, the way it displays arrays is an arbitrary choice of Google developers. Look, printing things has nothing to do with writing source code, indeed, you have the power !

printArray([1, 2, 3]);

function printArray(a) {
  console.log("[" 
  + a.join(", ") + ", "
  + "length: " + a.length 
  + "]");
}

Further reading : https://stackoverflow.com/a/5048482/1636522.

5
votes

Because arrays in Javascript can have members (ie. the stuff in the array), but are also objects, and as such can have key/value properties.

This can be a little confusing to wrap your head around, so let's use the metaphor of shipping box:

enter image description here

The box has stuff to be shipped inside it (the members), but it also has information on the box itself (the shipping address, return address, etc. ... ie. the properties). However, they also have a "shipping manifest" on their outside which details the contents inside the box, which makes it so that the contents of the box are also properties of it.

var box = [thing1ToShip, thing2ToShip]; // box contents
box.shippingAddress = 'some address'; // a box property (aka box['shippingAddress'])
box.returnAddress = 'some address'; // a box property (aka box['returnAddress'])
// The "manifest"
box[0] === thing1ToShip; // both content AND a box property
box[1] === thing2ToShip; // both content AND a box property
box.length === 2; // also a box property

For/of loops iterate over the iterable property of the provided object, and thus only return the values in the array itself. This is because objects in Javascript get to define how they should be iterated through, and Arrays define themselves to iterate based on length, not keys.

For/in loops iterate over the enumerable properties of the provided object. Because the members of an array are also properties (a[0]) you get both the key/value pairs you assigned and the array members.

As for:

var b=[1,2,3,foo: "bar"]

it gives me unexpected token . Why is it so?

That's simply because you are writing invalid Javascript. It is not possible to define the name/value properties of an Array when you create it, only its members. To add properties you have to do it after creating the Array (using the proper [thing1, thing2, thing3] syntax).

If you want to concisely add properties to a new Array you can use Object.assign:

var b = Object.assign([1,2,3], {foo: "bar"});

In other words start by creating an array, [1,2,3], then assign the properties of the second argument ({foo: "bar"}) to it ... and since there's only one property in that object that amounts to "add a foo: 'bar'" property to the array.