Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Fun with JavaScript Arrays (mikota.cz)
95 points by bubersson on June 12, 2013 | hide | past | favorite | 33 comments


TBH, all those pose nothing really unexpected. It all makes sense. I was a real JS hater, too - just because I didn't understand the language and I would have laughed at those examples - until I watched "Crockford on JavaScript." It's an interesting, entertaining, educating, (quite longish) series you should treat yourself to. And just as drostie said, given the way the language was born, it's really an amazing thing. And yes, it does have ugly parts.


I think I understand the language quite well and I still think it's terrible. Knowledge does not preclude disapproval.


Bear in mind that I know very little JavaScript so I may be completely wrong or this may be a very simple observation, but isn't the confusion in the first part simply due to the printed representation of the Array prototype/object?

I seem to remember that arrays in JavaScript are in theory just objects/dicts such that:

    var a = [1, 2, 3];
is equivalent to

    var a = {0 : 1, 1: 2, 2: 3};
and whatever function is invoked by evaluating `a` in the console must just iterate over the sorted keys of the object, starting at key = 0 and attempting to print key + 1 until the key does not exist. So he may be setting `a[-1]` and it happily exists, but the confusion is just down to the whatever the equivalent of the `__repr__` function is for JavaScript arrays.


Yes, most of the confusion is the author's misunderstanding of how arrays work in JS, combined with his tool not showing everything he put into the array.

You're correct that arrays are objects, which means they consist key/value pairs. Keys must be strings; values can be anything. So when you say `a[0] = 3;`, JS converts that to `a["0"] = 3;` and stores `{ "0": 3 }`.

So all those "funny" examples the author provides are just variants on converting an expression to a string. For example, `a[typeof a] = 4` is equivalent to `a["object"] = 4` (or `a.object = 4`) because `typeof a` is "object".

There's one wrinkle on this, though: objects and arrays aren't exactly the same thing. Arrays are a special kind of object, as far as JS is concerned. JS maintains a special "length" property for arrays. It doesn't do that for other objects.

(The same applies to JS functions and Regexes. They're objects, but they're special kinds of objects that can do things regular objects can't.)


Yes, most of the confusion is the author's misunderstanding of how arrays work in JS, combined with his tool not showing everything he put into the array.

There is nothing in the post that shows that author doesn't know how arrays are represented internally. Moreover, whether or not he does is largely irrelevant. He shows clear examples of bad language design, and they remain such whether you're aware of the inner workings of the language or not.

JavaScript's arrays have issues that span far beyond mere "string representation". For example, you can't safely use for...in construct to iterate over items in an array.


The `for … in` statement iterates over the enumerable properties of an object¹. It’s not for arrays.

1. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


I think you missed this bit at the beginning of the article:

"Also keep in mind that sometimes I'm playing stupid, but sometimes I'm actually stupid."

I suspect the author knows exactly how arrays work in JS and is just mucking about to demonstrate one of the "Bad Parts". This is pretty much in the same vein as the WAT video (https://www.destroyallsoftware.com/talks/wat)


> JS maintains a special "length" property for arrays. It doesn't do that for other objects. (The same applies to JS functions and Regexes. They're objects, but they're special kinds of objects that can do things regular objects can't.)

Indeed JS functions have a special `length' property too, where one can check the # of arguments specified in the function definition.


Not just the length property, but also a special array API (push, pop, etc). So there is no equivalence between an array and the simple object constructed from its keys and values, but the object would correctly represent a snapshot of the subset of array key-value pairs.


Technically, the special API is because arrays extend Array.prototype. You could create an object that does the same, and push(), pop(), etc would work.*

Try going to http://www.objectplayground.com and putting in the following code, selecting the "Show built-in objects" option, and evaluating:

  this.array = [];
  this.object = Object.create(Array.prototype);

  this.object.push("foo");
  this.object.push("bar");
*Well, they sort of work. They use and modify the "length" property, so you could see some weird behavior under some circumstances. Not recommended for production use. :-)


Yes, but a literal will still be either an array or an object, so that

  var arr = ['a','b']
will be different from

  var obj = {0:'a',1:'b'}


I can't believe no one has posted the (in)famous WAT ruby/javascript presentation: https://www.destroyallsoftware.com/talks/wat

It describes many ridiculous things in javascript such as empty array + empty array == empty string, etc. It is also great for a good laugh to anyone who has ever written javascript or ruby.


Many of those examples also make sense when you consider what you're actually asking for.

For example, Arrays have no + operator. So []+[] requires that [] be converted to a string first. The string representation of [] is "" (although some interpreters use "[]" or "[Array]" for clarity), so []+[]==""+""=="".

Similarly, he used the example []+{}, and claimed it was [object Object]. This actually wrong (at least in Chrome); it's actually "[object Object]", a string. The reason for this is the same as the previous example.

However, there is a different reason why {}+[]==0, given that "[object Object]"+[] == "[object Object]". There turns out to be something special about {} at the beginning of a statement (It's a block, or something). Thus, {}+[] is like {};+[], where +[] causes [] to be converted to a number: 0. Compare: {}=={} (syntax error, unexpected token ==). Yes, this one is actually confusing. (However, expressions are not usually evaluated in void context.)

He also observes that ",,,,,,,,,,,,,,," is a bad representation of Array(16). It is, since the undefined in each element is important, but again, that can be solved by using an interpreter with better string generation. (Or a version of IE that doesn't attempt to be clever and just returns "[Object]" all the time).

So perhaps the moral of the story is: Yes, it's an entertaining talk, but a several of the examples break down because his interpreter is vague.


Assignment within the brackets, that kinda surprised me

  > a = []
  > a[a[typeof a]='huh', void a] = 'puh'

  > console.log(a)
  [object: "huh", undefined: "puh"]
huh... you can do this too

  > a = []
  > a[a[a[typeof a]='huh'] = 'puh'] = 'woh'
  > console.log(a)
  [object: "huh", huh: "puh", puh: "woh"]


The comma operator in Javascript is a major WTF. The entire post basically comes down to a, b evaluates to b, [] coerces to string, and arrays are objects.

The only thing to really look bad is chrome, which is surprisingly easy to kill.


  > a.length
  0
Same as the article, you are adding properties to a. A is an array, not an hashmap. Any index that is not a number will create a property, not a new index.


You don't have to stretch it beyond normal tasks to get some big WTFs:

https://gist.github.com/hcarvalhoalves/4471029


Those are only little WTFs. The fact that object-equality is being tested for, for example, is not a WTF on its own; it's a valid concern in any non-functional language (answering the important question, "will editing X affect Y?").

The fact that everything has a string representation which gives everything a natural lexicographical ordering (which is inconsistent with the value-ordering of numbers) is also not a WTF, especially because `array.sort(function (x, y) { return x - y; })` does precisely what you want extensibly. Maybe the fact that there is no `<>` operator to represent `x >= y && x <= y` (which would basically test if x.toString() == y.toString() for the general ordering) might be a WTF, if you're expecting == to be consistent with <=. But the general idea of "we're not going to try to be smart, we'll just use strings" is actually very Unix-y. The alternative is to throw errors whenever you try to sort arrays which have incompatible types -- but JS doesn't have a strong notion of types.

Don't get me wrong, I'd love it if JS were different in many ways -- especially if it were more functional, because so much of the core language is built around functional concepts. There are also now type systems which are being developed which allow for the 'duck typing' style that JS uses for everything; it would have been nice if Eich had been able to magically foresee it back when it was LiveScript.

But for a language which was a little haphazardly thrown together at first and then was haphazardly canonized by two competing companies during the Browser Wars, it's actually done remarkably well.


sort() is a bit weird, I have to agree, but the '==' thing is a common problem to a lot of languages, not exactly a js-specific WTF.

Off the top of my mind:

In Python, '==' on arrays compares elements.

In Java, '==' on arrays compares the objects themselves. You have to use equals() to compare the elements.

In C(++) arrays, '==' compares the objects. But for std::vector, it compares the elements.

One could argue it's a language choice more than a screwup.


Common Lisp has quite a few equality functions: eq, eql, equal, equalp

http://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node74.html


The point is not what "==" should do. The point is you have even two equality operators - with subtle differences - and still no way to compare two arrays by value without boilerplate, which should be trivial.


The default lexicographical sort order on an array of integers is unexpected, yes.


Specially given the fact value-ordering is also lexicographical for the ASCII set, and for Unicode it gets it wrong anyway.


That is not a WTF.

A real WTF would be the opposite, two arrays comparing as equal because their values match. Like the brain-dead PHP comparison.


You want operators for both: test if it´s the same object (identity) and test if they are equivalent (equality).


Lol, why isn't that fixed for javascript? That's beyond screwy.


If

  var a = [1, 2, 3]
  a == a
Returned false, it would by screwy. In this case the == operator behaves like the `is` operator in Python when comparing lists.


Once you realize that there are no associative arrays in JavaScript these things become less of a WTF.


Couldn't some JavaScript Objects be called associative arrays?


Not really no. Every object has it's prototype set to the object prototype. Which means that even if you create a new empty object, it inherits functions and values from the object protype.

var a = {}; typeof(a.hasOwnProperty) === "function"

Objects are very often used as associative arrays, quite simply becouse objects and arrays are all js has to offer. Javascript Harmony will change that though, with it's own Map type.


Not really. Actually you can create an empty object by: var a = Object.create(null)


oooo, shiny!


var a = {};

PHP != Javascript




Consider applying for YC's Summer 2026 batch! Applications are open till May 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: