4

Why does javascript allow me to do the following.

a = {two : 'World'};
a[1] = 'Hello';
console.log(a[1]);
console.log(a.two);

the output is

Hello
World

Shouldn't it complain that I am trying to use an object as an Array? This works with anything by the way, like so

b = new Date();
b[1] = 'Wow';
console.log(b[1]);

the output is

wow

Is there a use for this? It seems to me like a bad programing practice.

puk
  • 16,318
  • 29
  • 119
  • 199
  • In javascript, everything is an object so you can use just about anything as an object - arrays, dates etc. However, it is considered bad practice to use an array, date, whatever where a plain object (i.e. one created by the Object constructor) will do the job. – RobG Jun 14 '11 at 23:46
  • @RobG I'm confused, I'm assuming you are saying it's bad to construct an object as such `x = new Object()` and not as such `x = new Array()` – puk Jun 15 '11 at 02:00
  • puk - I'm confused! :-) My point was that an array should only be used where the special properties of an array are required (e.g. ordered access to indexed properties). If just general storage of name/value pairs is required, a plain object should be used. Also, it's generally considered better to use literals: `var x = []` or `var y = {}` but the outcome is identical to using `var x = new Object()` or `var y = new Array()`. – RobG Jun 15 '11 at 04:06
  • @RobG I prefer the literals because they are easier to understand, and more compact. But it still blows my mind that javascript treats objects and arrays as the same thing. Python has Objects, lists, and dictionaries, which are kinda like JS arrays, but their is a clear line in the sand between objects (class instances) and lists/dictionaries. – puk Jun 15 '11 at 04:56

5 Answers5

4

In Javascript, all arrays are objects. There is no hard-and-fast dividing line between the two. Arrays have certain properties and methods, but are implemented as objects.

The syntax [1] is one of the two equivalent Javascript member operators. These two are equivalent:

var foo = {};
foo.bar = 'foobar';
foo['bar'] = 'foobar';

However, with the dot notation (foo.bar), you can only access properties that are valid Javascript identifiers. This means:

a sequence of alphanumerical characters, also including the underscore ("_") and dollar sign ("$"), that cannot start with a number (source)

You can set the properties of any Javascript object -- array, object, Date object, string, number -- in this fashion, since they all derive from the same Object type.

lonesomeday
  • 233,373
  • 50
  • 316
  • 318
  • the first two lines are confusing in this context. Yes, all arrays are objects, but that has nothing to do with this answer :) –  Jun 14 '11 at 23:20
  • So many good answers, I don't know which to choose. Thanx guys. I see now that javascript doesn't have true arrays like contiguous memory allocation in C. – puk Jun 15 '11 at 02:04
  • @cwolves All arrays are objects, and all objects can have arbitrary properties specified on them. – lonesomeday Jun 15 '11 at 06:43
  • Yeah, but the question is about the other direction, about why an object acts like an array. –  Jun 15 '11 at 06:45
  • @cwolves Objects act like arrays, because arrays act like objects, because arrays are objects... – lonesomeday Jun 15 '11 at 06:53
  • yeah, I know that. My point is that the statement "all arrays are objects" has nothing to do with the question. Whatever, :) –  Jun 15 '11 at 06:55
  • @cwolves I think it has *something* to do with the question. Perhaps it's a bit of a roundabout way of justifying the behaviour! Thanks for your thoughts, though -- appreciated. – lonesomeday Jun 15 '11 at 06:57
3

Shouldn't it complain that I am trying to use an object as an Array?

No. Numeric properties are allowed. The square bracket notation [] is used on Objects for keys that are not valid javascript identifiers. Not just on Arrays.

a[ "some-invalid***identifier" ] = 'some value';

This works with anything by the way, like so

Yes, same reason. new Date() returns an object to which properties can be assigned either via dot or square bracket notation.

user113716
  • 318,772
  • 63
  • 451
  • 440
2

You're not treating an object like an array - you're using numeric keys in an object. Just as these are both valid:

var o = { "test":1 };
o["test2"] = 2;

so are these:

var o = { 1: "test" };
o[2] = "test2";

Edit: As the comment below points out, the above syntax is actually misleading, as 1 gets converted to a string in both cases - so technically, this is exactly the same as

var o = { "1": "test" };

as you can see here:

var o = {1:"test"};
for (i in o) console.log(i, i===1, i==="1"); // 1 false true
nrabinowitz
  • 55,314
  • 10
  • 149
  • 165
  • 1
    Small correction, it's technically `{'1': 'test'}` -- all object keys are strings –  Jun 14 '11 at 23:19
  • Good point - edited accordingly. Weirdly enough, this seems to be true for arrays as well - the same test on an array key returns `1 false true` too. – nrabinowitz Jun 14 '11 at 23:52
  • that's because of how arrays are implemented. Run this in a modern browser to see what I mean: `var x = {'0':'A', '1':'B', '2':'C'}; Object.defineProperty(x, 'length', {value:3, writable:true, configurable:true}); x.__proto__ = Array.prototype;` `x` now ___is___ an array –  Jun 15 '11 at 00:01
0
Because the [key] notation to access the properties of an object will accept anything as a key, including a numeric. Other things you can use include: var a={}, b={}; a{b}=1; // object a's property is the object 'b'. Rather than bad programming practice, it provides a truly unified model for objects.


See comments.

Rob Raisch
  • 17,040
  • 4
  • 48
  • 58
  • Not quite true. Keys can only be strings, but JavaScript will cast anything into a string. –  Jun 14 '11 at 23:20
  • 1
    Also, your example is dangerous: `var a = {}, b={}; a[b] = 1; a[a] = 1; a[{}] = 1; // a == {'[object Object]' : 1}}` -- it's not assigning the object "b" as a key, it's assigning "[object Object]" as a key –  Jun 14 '11 at 23:27
  • I prefer to keep lists/arrays separate from properties. I was stumped as to how I didn't have a `x = new Array()` call anywhere in my code, but `x[i]=123` still worked. I just don't see why they couldn't use a different symbol for object properties ie `array[0]='first entry'` and `property{x} = 'property eks'` – puk Jun 15 '11 at 02:09
  • @cwolves, I agree and would delete my solution but for the very cogent insight expressed here in its comments. – Rob Raisch Jun 15 '11 at 15:42
0

Honestly, I think the console output of this can be pretty enlightening:

var a = {two: "World"};
a[1] = "Hello";
console.log(a);
//   1: "Hello"
//   two: "World"
// > __proto__: Object

var b = new Date();
b[1] = 'Wow';
console.dir(b[1]); // console.log just returns the timestamp
//   1: "Wow"
// v __proto__: Date
//   > constructor: function Date() { [native code] }
//   > getDate: ...

As you can see, these are both just objects, one inheriting from the generic Object and the other from Date. Just like any other object, they can be assigned a key/value pair, where your 1 is just being converted into a string.

It's definitely messy coding if you use it that way, but it's a pretty important illustration of JavaScript principles. There are plenty of useful ways to extend objects that are syntactically similar to what you're doing.

brymck
  • 7,555
  • 28
  • 31
  • I'm a C/Python guy, so messing with the array/list notation is blasphemy. – puk Jun 15 '11 at 02:10
  • @puk, right, I'm talking more about how what you're doing here is the same as how you'd extend objects. Also, these are neither arrays (`[]`, which do have more restrictions on assignment) nor lists (which are merely _approximated_). You're really adding properties to a single instance of an object. Your examples are, of course, bad practice, but if you instead did `var date = new Date(); date['getAMPM'] = function() { return this.getHours() > 11 ? "PM" : "AM"; };` (or more likely `date.getAMPM =`), assuming it were useful, no one would bat an eye. – brymck Jun 15 '11 at 02:36