72 comments

[ 2.2 ms ] story [ 135 ms ] thread
This is a good way to distinguish between someone who knows JavaScript the language from someone who can apply JavaScript to perform DOM manipulation and data requests.

Not that there's anything inherently wrong with the latter.

I fall firmly in that latter camp; I do all my algorithmic work and heavy lifting in C# and just use JS to get data onto the client and display it. But it's easy for someone like to me to start believing "I know JavaScript" when in reality, it's more accurate to say "I can use JavaScript in the browser for common client/server cases". I'm currently working through "Effective JavaScript" to help improve on this.

This isn't really true.

There are some programmers who are walking encyclopaedias of language features. It is absolutely no guarantee that they are good coders. Some simply can't code at all, they know all the motions but cannot see how to thread them together past the simplest of programs. Others cannot distinguish between useful language features and terrible ones, using them indiscriminately and making a terrible incomprehensible mess. And of course some are simply the best programmers you will ever meet.

Javascript is a language full of terrible design decisions. Knowing them all doesn't make you a good coder, in fact simply being taught only the safe parts will often make a better coder than knowing a lot of the results of these parlour tricks.

Agreed, I use Java for my heavy lifting. Use JS to display the data, provide some client side validation on forms, switch some classes around or manipulate something in the DOM using JQuery or the like.

I cant create a full fledged client side JS app but I dont really need to if I have a larger language helping on the server side.

I create fully fledged JS apps on a big scale, the difference from this test to reality is startling. The difference is I enforce the use of the closure compiler in our team so 99% of these issues are caught by the compiler. end of story. this test is bullshit.
What exactly do you mean by "fully fledged JS apps on a big scale"?

When it comes to more traditional languages like C, C++ and Java, "fully fledged" and "big scale" are descriptors usually reserved for multi-million line systems consistently worked on by 50+ full-time developers at a time.

I'm very suspicious of such terminology being applied to JavaScript applications. We generally see a much, much smaller scale when it comes to those. The "high end" cases are usually topping out at 100,000 to 200,000 lines of code at most, and perhaps 10 or 20 developers. They're a far cry from the C, C++, Java and C# examples.

Is any of this code you've worked on publicly available?

(comment deleted)
i mean teams of 20-25 people, working with millions of lines of code over all the modules, that has to be regressed the whole time. Big for the tech stack it is. This is not counting our bucketloads of java and scala code as well.
What benefits do JS apps have over a server-side language such as Java, C# or Ruby? Especially in an enterprise environment.

In most of my limited experience JS has been harder to work with than almost anything I have encountered. My personal experience using JS client side apps has left me feeling sour. Every time I visit a webpage and immediately hit a JS loading screen I cringe. Gmail is about the only I dont completely dislike that I know is written in JS.

we use java to render our templates on the server side. https://developers.google.com/closure/templates/ And yes response times, it makes a massive massive difference in perceived responses. Our stack is the same stack which gmail uses (we use the full closure suite so compiler, templates, stylesheets, linter and library)
Not that I am so sure about he usefulness of this, but there is at least a double of this question:

  [typeof null, null instanceof Object]
I got it right the second time!
Me too.
I found these questions awkwardly posed. This could just be "what is the `typeof null`" and not be two questions in one in the form of an array.
In addition to that, the correction text only explain the first element of the array.
Holy fuckaroni JavaScript is a mess of a language. I did terrible on this test.
Agreed. I don't want to ever show my score.
Agreed. I already made arrangements to have my things shipped to Alaska where i will live out the rest of my days .. this "programming" thing didn't pan out.
It's not a language it's a set of non-binding promises that certain words and characters in certain combinations may produce some sort of effect.
I wish there was a 'JavaScript Under the Microscope' like 'Ruby Under the Microscope' by Pat Shaughnessy.

I think I would have a much better understanding of JavaScript if I knew how a good javascript engine works.

> I think I would have a much better understanding of JavaScript if I knew how a good javascript engine works.

So would the people who write the good javascript engines.

I'm joking but only partially as they have to write an engine against both a standard and a convention plus support all the hacks going back years.

It's a mess but what I'm slowly and painfully learning is that if you avoid the shoals and hidden wrecks you can do some cool stuff with JS.

One easy step in the right direction is a quality build process that includes linting and strong error/warning reporting. Push your code through the Closure compiler and keep JSHint (or your favorite linter) active in your editor and you're already taking a strong step to better JavaScript.
The author of this quiz could have made this a great learning opportunity by linking to the appropriate sections in ECMA-262[0].

For example, question 15 makes a lot more sense when you read "ToBoolean" (Sec 9.2) which shows that all objects (including arrays) are always considered truthy values, however when compared with ==, "The Abstract Equality Comparison Algorithm" (11.9.3) is used, which gives this behavior:

  >> [0] == [0]
  false
  >> a = [0]; a == a
  true
because a shallow comparison is used.

[0] http://www.ecma-international.org/publications/files/ECMA-ST...

So it's like Scheme's "eq?", except less useful?
Wow, i thought i was really good at javascript. I tried 7 questions at random and got 6 of them wrong.

/me sad face

Same here. JS has some seriously bizarre gotchas.
It feels more like an interactive version of wtfjs.
I really wouldn't draw any larger conclusions about Javascript from these examples or from the results of this test.

These are edge cases.

every language has them

No sane programmer would put anything like these examples in a normal program, and knowing these edge cases doesn't really help you solve problems or get things done. Because you just won't encounter them in real life.

I wouldn't say that.

The really nasty bugs come exactly from examples like these. This can save you hours at some point, I had to find some of them the hard way.

typeof is broken.

instanceOf is broken.

precision arithmetic on floats is broken

var hoisting and function scope is a misfeature

It's more useful to know those facts than to know the exact result of them in individual contexts well enough to answer a multiple choice question.

It's more useful, if you see these examples in real life, to just rewrite them from scratch to not use the broken parts of javascript.

Javascript has pitfalls and ugly parts. they can be avoided. this test gives you no great insight into them, or how to avoid them.

Yeap but the most probable way to learn about them is to lose several hours debugging each of them.

I don't think there is other language that comes close to javascript. Ok it's everywhere now, you make a lot of money with it, that's what we have now, but you and others defending it make my ears bleed.

Well even if something is "broken" you have to use it once in a while, how can you avoid function scope and typeof?

Also sometimes you have to maintain code, so it's not a choice.

There are always explanations that are helpful. You have to know about the pitfalls to be able to avoid them.

The questions just make you study the examples and also it's more fun this way.

I actually totally agree with you. But if this is your only exposure to these things then I can't expect you'd gain that much insight.
how is function scope a misfeature?
"typeof is broken. instanceOf is broken. precision arithmetic on floats is broken var hoisting and function scope is a misfeature"

Sounds like one hell of a PR pitch. :)

I got 15/37, in no small part by guessing "what's the most bizarre and inconsistent thing that could happen here"? Apparently my imagination is less vivid than JS's.

I'm not saying Javascript is perfect. just that you can't draw many useful conclusions from a document that sets out to find specifically its weirdest (mis)features.

And actually you could avoid using "typeof" by using polyfills/builtins for the new "is[Type]" range of functions in ecmascript5. or a similar one from a library.

I disagree, several of these issues can come in up innocent programs written by less-seasoned practitioners. For example, [] not ==ing [] seems like it would be an incredibly common noob mistake. Some of the auto-conversion rules make certain classes of true bugs difficult to find. And I could see someone from a more 'traditional' OOP background getting bitten by "string" != new String("string") != String("string).

I will give you that many of these test cases are simply looking for trouble, especially the examples explicitly monkeying around with, especially anything involving direct manipulation of prototypes. I am also personally not bothered by most numeric wonkyness because that's floats in general, not JavaScript in particular (though I wish it had true Ints).

when you realize that "[]" is the same as "new Array()", it's not at all confusing.

however, typeof NaN == "number" is a major wtf.

Every language has /some/ edge cases. Javascript is one big broken edge case.
I hear that kind of sentiment a lot here on hacker news. It doesn't make sense to me in the context of also giving C, C++ and PHP a pass. Javascript is a paragon of design genius compared to those languages.

Especially PHP, which nobody is even forced to use. You could use any language on the server!

Yes, but what other common language besides PHP has so many ridiculous edge cases? None I use.
Perl would likely be the next closest, but even it's nowhere near as bad as PHP and JavaScript are.
Ruby has about as many. So does C, and C++, which each have much more, with the extra twist that C and C++'s edge cases are wildly different depending on which compiler you use and which platform, and this is labelled a "feature".

and Python. And Perl of course. Bash.

I bet you could make a similar quiz for Java too.

If anything varies between compilers in C/C++ then you're using undefined behavior. Not the language's fault. The edge cases of these languages are less jarring then Javascript's and make more sense.
To be honest, I spent half an hour today debugging some JavaScript I'd written recently that looked something like this:

    var array = ["a", "b"];
    var otherArray = ["c", "d"];
    otherArray.forEach(array.push, array);
    console.log(array);
I expected array to become ["a", "b", "c", "d"], but it actually becomes ["a", "b", "c", 0, <Array>, "d", 1, <Array>]. This is because Array#push accepts 1 through n arguments, not just 1 argument.

(I couldn't use Array#concat for reasons that don't show up in this simplified example.)

push was designed and made about a decade before "forEach".

Your code sample is not surprising or obscure to someone who has skimmed documentation on "push" and "forEach"

(comment deleted)
This line:

  [ [3,2,1].reduce(Math.pow), [].reduce(Math.pow)] ]
has an extra "]".
This question is wrong:

    What is the result of this expression? (or multiple ones)
    
    "1 2 3".replace(/d/g, parseInt)
It says the correct answer is "1 NaN 3" but the real correct answer is "1 2 3". I assume it's a typo of:

    "1 2 3".replace(/\d/g, parseInt)
In addition, for several questions, such as this:

    var x = [].reverse; x();
Their 'official' answer is only true outside of strict mode. So to add to TheZenPsycho's comments, not only are these edge cases, many are edge cases that are known and that strict mode tries to solve.

I also object to []==[] being false as evidence that == is the spawn of Satan. == may be weird, but this is a case where === does the same thing. You just have to understand that [] and {} (and regex literals) construct new objects, and objects are compared by reference, not by value.

And one that has the right answer but explains it incorrectly:

> What is the result of this expression? (or multiple ones)

  [1 < 2 < 3, 3 < 2 < 1]
  __[true, true]__ [true, false] error other
> Implicit conversions at work. both true and false are greater than any number.

True and false are greater than any number? What? This is not only not correct, it is contradicted by the answer. The real explanation is that 1 < 2 is true, which converts to 1, and thus less than 3; meanwhile, 3 < 2 is false, which converts to 0, and thus less than 1. This is actually the same result you would get in C.

For the

  function f() {}
  var parent = Object.getPrototypeOf(f);
  typeof eval(parent.name)
one, this is also wrong. I can't find anywhere in the spec that a name is required for Function.prototype[1], and 15.3.4.2 actually says that Function.prototype.toString() can return "An implementation-dependent representation of the function". Spidermonkey assigns it no name, so

  typeof eval(parent.name)
just returns 'undefined', it doesn't throw an error. There's certainly no requirement that it be invocable by name, and, indeed, if v8 had left a function named 'Empty' in the global scope, that would have been bad.

[1] http://es5.github.io/#x15.3.4

Also, using 'class' as an IdentifierName is incorrectly explained in the answer for

  var a = {class: "Animal", name: 'Fido'};
  a.class
as this will correctly return "Animal" in most browsers. It may be true that it's buggy in IE, but that's a bug, not part of the language. 'class' is a reserved word, but only as an Identifier, not an IdentifierName.

That might seem like a confusing splitting of hairs, but that's just due to the fact this is from a spec, not an explanation. In practice it basically means that you can't declare a variable with a reserved word name, but something like a property name (like a.class, or used when initializing a variable via an object literal) is fine using a reserved word.

In fact, the difference between an Identifier and an IdentifierName has allowed more than a few of the changes coming in ECMAScript 6 to be backwards compatible.

Even when not excusing browser bugs, there's a lot of sloppiness in this quiz.

`a.class` is explicitly legal in ES5, which is the spec that explicitly allows trailing commas in the `[,,,].join(", ")` question, so it's at least a bit confused about which version of JavaScript it's quizzing.
So what's a good score? I got just under 50% and was considered a "Working Practitioner." Not as high as I'd have hoped, but I'm in a hurry to get home.
"it goes into an infinite loop, 2^53 is the highest possible number in javascript, and 2^53+1 gives 2^53, so i can never become larger than that."

This is wrong. 2^53 is the highest integer value you can represent exactly with a 64-bit float without truncation, but much larger numbers are available, spaced more than 1 unit apart.

So 2^53 == 2^53 + 1 != 2^53 + 2 != 2^53 + 3 == 2^53 + 4 == 2^53 + 5 != 2^53 + 6.

> 2^53 is the highest integer value you can represent exactly with a 64-bit float without truncation, but much larger numbers are available, spaced more than 1 unit apart.

Yeah, that one bothered me as well, though to be fair, it is a hard condition to phrase. You also gave a definition that isn't quite right, as 2^53 + 2 is an integer value that can be represented exactly with a 64-bit float without truncation :) Perhaps you meant "exactly" in a somewhat more nuanced way.

You'd have to phrase it as something like every positive integer less than or equal to 2^53 can be exactly represented with a double precision float. Of course, there are more positive integers above 2^53 that can be exactly represented by a double than there are below, they're just spread farther and farther apart, as you said.

I may be alone in this camp, but I'd actually recommend people read the ES5 spec if they want to be confident in their JS. It's one of the most readable and understandable specifications I've read, and (compared to behemoths like C++) is fairly short. There's even an internally-hyperlinked version at http://es5.github.io .
Yes, but I don't know the answers to these questions because I know to avoid code like this.
Sure, some of these examples are pretty academic (eg ` Array.isArray( Array.prototype )`). But can you truthfully say you never get bitten by basic JS arithmetic fails, given how many ways it's possible to accidentally coerce numbers to strings?
Years ago, perhaps. Can't remember a specific instance.
I got two out of two and then stopped. This is another "look how many weird results we can get out of javascript" exercise.

Gary Bernhardt covered this humorously here: https://www.destroyallsoftware.com/talks/wat

And Crockford covers some issues more formally in his talks.

The takeaway is that if you do something that can possibly be interpreted in a strange way, javascript is mostly likely going to interpret it in a way that you didn't expect. You could say it sucks because of that and/or you can look at the ecmascript standard and try to reason about it. But either way, it's best not to do anything even slightly ambiguous. Stick to the good parts.

Can we please stop pretending JavaScript is some esoteric language?
I don't think that anyone really considers it "esoteric". There's not really any secret to it. Once one accepts that it's just a very poorly designed language, it makes perfect sense why it's so heavily broken in so many critical ways.
I'd never heard of reduce, filter, and some others here, although I've written an emulator and a 3D game in JS. I guess sticking with a subset of JS you know to be safe is good. Using good libraries like jQuery too.
"1 2 3".replace(/d/g, parseInt), I guess it should be "1 2 3".replace(/+d/g, parseInt)
What's the logic of this one:

1 + - + + + - + 1

One plus (negative of ( negative one))? (and the successive pluses don't matter?)