JavaScript Is Weird
jsisweird.com
jsisweird.com
That's not really a JS problem, that's a floating point problem. Plenty of languages will have the same issue.
+!![]
"" - - ""
(null - 0) + "0"
Calling these things weird is fair enough but I can't help thinking this is code you'd never actually write outside of the context of a "Look how weird JS is!" post. It's like picking examples from the Annual Obfuscated C Contest to show how hard it is to understand C. Yes, some languages enable you to write weird garbage code that's hard to reason about, and ideally they wouldn't because enforcing sane code would be great, but come on. Most of these things aren't a big problem that we suffer every day. Just be moderately careful about casting things from one data type to another and all these problems go away.
Turns out several months before, someone was doing some refactoring, moved some methods around, but also changed a "==" to a "===" in the process. Generally a good idea, but it slipped through to production without anyone noticing or breaking any tests.
The issue ended up being that a rare code path in a tangentially related method would cause that method to return a float instead of an int. This propagated through, eventually causing a check of 0.0 === 0 to fail where previously 0.0 == 0 passed.
For the same reason that, as the saying goes, there is no such thing as a "compiled language", no, "C" doesn't give you a warning about types. The compiler you're using gives you a warning. If you want a typechecking pass over JS, then use a tool that does typechecking. Eschewing with a typechecker and then complaining about the lack of type mismatch warnings, however, makes little sense.
What bloat are you referring to? TS compiles down to plain JS.
Like most bad things, it's only around because a big company said so.
Like all bad things in JS, there was a push to remove it at ECMA, but Microsoft had reverse-engineered JS into JScript and refused to go along with the changes to fix the weirdness.
Even Javascript's weak typing makes sense in this case. Why automatically convert everything? Becausethe expectation was that inputs to your Javascript would be HTML attributes which are all strings. Automating type conversions from strings made sense. But once you move to larger scale programing, Javascript's weak typing is awful
If a user does need to do scientific computing, they could use a "# use-floating-point" pragma or something so that literals get interpreted as floating point. Otherwise, we map them to rationals.
Of course, doing rational arithmetic is much slower (for example, Gaussian elimination is exponential for arbitrary precision rationals, while it's famously O(n^3) for floating point), so there's a danger of people accidentally using it in triply nested loops etc., but I have a feeling that if you need to do that kind of thing you know that you might run into performance issues and you think twice.
Arbitrary-precision rationals would get extremely hairy extremely quickly and simply break down for a huge number of use cases. People use exponentiation, square roots, and compound interest! If a "senior developer" who actually works with real account balances doesn't understand floating point, why would you expect them to understand why they can take the square root of 4, but the square root of 2 crashes their application? Or why representing the total interest on a 30-year loan at 3.5% takes over 400 bits?
The reality is that software engineers need to understand how computers work for the field they're working in. Many (if not most) programmers will never encounter a situation where they're responsible for tracking real account balances. The ones that do simply need to know how to do their job.
In modern JavaScript, you can use BigInt literals by suffixing an n, like this:
const maxPlusOne = 9007199254740992n;
If I could magically redesign the language, I would make all integer literals be effectively BigInt values, and I would make all decimal literals be effectively decimal values so that 0.1 + 0.2 === 0.3. I would reserve the letter suffixes for more performant types like 64-bit floating point numbers that have surprising behaviour.I haven't come across the "pragma" directive in a Javascript context. I guess it's another new feature (I have trouble keeping up these days).
Sadly, most people don't understand the distinctions, so this will never happen. (Even in reply to your post people keep talking about "decimals", as if the number base is at all relevant here.)
The number base is relevant, because money is discrete, and measured in units of exactly 1/10^n, and if you try to use floating point numbers to represent that you will cause your future self a world of pain.
This is technically impossible. Almost all of the real numbers can't be represented in a computer. The most you can get is the computable reals. But to be able to represent some of those and to do arithmetic on them, you have to use somewhat complicated representations such as Cauchy sequences.
The use cases for computing with exact representations of (computable) irrational numbers are fairly limited (probably mostly restricted to symbolic algebra). In 99% of cases, if you need the square root of 2 that's probably because this comes from some sort of measurement (e.g. you need the diagonal of a square with sides 1), and if it's a measurement, there's going to be an error associated with it anyway and there's no point in insisting that this number that you measured is "exactly" sqrt(2).
This is different from rational (and, in particular, integer) numbers, in which case there are many valid use cases for representing them exactly, e.g. money.
It's technically impossible for the integers too.
> Almost all of the real numbers can't be represented in a computer.
Almost all of the integers can't be represented in a computer.
What, exactly, is your point?
More accurately it's a binary problem. 0.1 and 0.3 have non-terminating representations in binary, so it's completely irrelevant whether you're using fixed or floating point.
Any number that can be represented as the sum of powers of 2 and 5 have a terminating decimal representation, whereas only numbers that can be represented as the sum of powers of 2 have a terminating representation in binary. The latter is clearly a subset of the former, so it seems obvious that we should be using decimal types by default in our programming languages.
Oh well.
> integer unscaled value + integer scale
binary float does the same, just using 2^exp instead of 10^exp for scale.
Note: I'm using a notation for infinitely repeating decimals that I learned in school - 0.(6) means 0.6666666...; 0.(01) means 0.010101010101...
https://iquilezles.org/www/articles/floatingbar/floatingbar....
No. "1", "3" and "10" can all fit easily in just four bits.
Just use rational numbers and solve the problem for good.
This will be a problem for any base representation when it has to be boxed in finite number of digits or memory.
One good thing is decimal is widely used format, so it is good to go with that for representing stuff. But, it is more of an accidental advantage that decimal has. Nothing more.
I think there are arguments to use decimal as representation is there, (where I originally came to know about this problem) [1].
I mean:
!!!true
In what language does that (or its equivalent) not evaluate to false?Kinda crazy to me that Go doesn't reserve the words "true" and "false", but ¯\_(ツ)_/¯
False, True = True, False # Have fun debugging!In that Go example above, the author is reassigning true and false to be their opposites.
Some people add two exclamation points as a shorthand to cast a value to a boolean. So if you wanted to see if something was 'truthy', you could say `!!truthyValue` and it would return ` true` instead of the value itself. Literally what your asking the language is "give me the opposite boolean value of the opposite boolean value of `truthyValue`"
Now you can probably see why three exclamation points is silly, it's not giving you anything that a single exclamation point wouldn't give you. Both `!truthyValue` and `!!!truthyValue` evaluate to false, you are literally saying "give me the opposite boolean value of the opposite boolean value of the opposite boolean value of `truthyValue`"
The example in Go is intentionally misleading because Go lets you reassign the values for `true` and `false`. It's going through all the same steps I described above, but it's starting with a value opposite of what you think it is
> Even if you're a JS developer, most of this syntax is probably, and hopefully, not something you use in your daily life.
So I think you should look at this site more as something fun you might not have known if you're an js developer than as criticism of js.
That being said, the !!"" isn't that weird of a syntax is it? I see and use the double exclamation mark all the time.
Most people arent going to "know" what '+!![]' will resolve to because it makes literally no sense to combine those operators into a single expression in anything approaching normal code.
The fact that [] is truthy is something everybody learns in their first weeks of JS programming otherwise you would be writing `if (someArray)` and wondering why your code is broken.
A weird one would be to explain why +[] is 0 and +{} is NaN. That is nonsensical.
> the !!"" isn't that weird of a syntax is it?
Why would someone say not not empty string in code somewhere? Or do you mean seeing !!var_that_could_have_empty_string isn't too weird?
perl -e 'print !!"whee"'
1
php -r 'print !!"whee";'
1
awk 'BEGIN {print !!"whee"}'
1Carmack-like people utilising this stuff for good are few and far between, for your everyday joe programmer this is a footgun, a very-nonobvious and non-intuitive one (hence footgun moniker) that they write in crunch, it slips past reviews because reviewers are just joes with couple extra years, if at all, and then whrn things break after deployment, its an absolute pain to debug.
That is the whole premise of the site, though. They even say that these examples aren't common syntax or patterns before you start.
am i being punkd?
https://stackoverflow.com/a/23465314
For me it’s only rows [[]], [0], [1], i.e. array-unfolding related, but all others are regular weak-typed comparisons like in perl and other dynamic semantics. <snip> Edit: just realized “if (array)” is okay, nevermind.
You never do them on purpose. The problem is when you do them by accident because of a mistake in your code, and the error slips through unnoticed, doing the wrong thing.
> weak-typed comparisons like in perl
Perl has separate operators for working on strings vs numbers, so you are always explicit about performing a numerical vs string comparison etc. Not so for JavaScript.
But the values which you compare do not have to be of the same type, and it does not end at scalars (which are really ephemeral in their exact typing even in native API, see perlapi). Basically it has two flavors of ==, each with its own preferred coercion. Perl also has contexts, e.g. boolean and scalar which can operate on lists and hashes (@list == 5). While the form is different, semantics are similar.
The problem is when you do them by accident because of a mistake in your code, and the error slips through unnoticed, doing the wrong thing
If your string array contains some [[0]] by accident, I’d say there is not much left to do anyway. === doesn’t report this either, it only has narrower trueness, which may decrease or increase an error surface depending on how your conditions are spelled. And to be clear, I’m not arguing against using === in places where identity^ or strict equality check is necessary or desired (you may desire it most of the times and that’s valid). My concern is that (a) everyone blames == anywhere, for completely zealous reasons, (b) I have to type a poem to test for null and calm down someone’s anxiety.
^ I know it’s not exactly identity, but is close enough for practical purposes
object.someField === null || object.someFeild === undefined
madness, or to a potential error if a programmer thinks that null can not be there.We could do an exception for null === undefined, but it’s against its spirit and will not be accepted.
^ languages that treat non-existent key as undefined are usually doomed to introduce Null atom in some form, or to work around that limitation constantly in metaprogramming
[1, 2, 3] + [4, 5, 6] // -> "1,2,34,5,6"
[,,,].length // -> 3
The second is only weird in that you are constructing an array with implicit undefined values, which is exactly what I would expect to happen if my linter didn't complain about the syntax and I had to guess what might be happening.
Like GP said, comes from other languages. That line can be copy/pasted into python and it performs concatenation.
> [1, 2, 3] + [4, 5, 6] // -> "1,2,34,5,6"
What would you expect instead?
> [,,,].length // -> 3
Is there any use case where you would want to deal with sparse arrays?
An addition operation over two numerical vectors of length 3.
I would expect the result to match its inputs and provide a numerical vector of length 3.
Thus: [5, 7, 9]
So if applying an operator to arrays spread the operation across all the elements that way, it would imply that the same should happen generally for all object properties, with whatever weird implications that would entail.
a.foo = 1
b.foo = 'there'
a + b // { foo: '1there' } ?> What would you expect instead?
If I let my first instinct speak:
[1,2,3,4,5,6]
And then if I think a little more, then maybe:
[5,7,9] //with obvious caveats
In no way do I expect what GP actually provided.
[1] https://tc39.es/ecma262/multipage/ecmascript-language-expres...
Not really. Now explain why [,,,].map((e,i) => i) is [,,,] instead of [1,2,3] please ;)
I don't think this is too weird if you think about it. JS allows trailing commas, so the last one is ignored. Effectively this is `[ undefined, undefined, undefined, ]`. A syntax error would have made sense here, but the length of three is a result of the usual syntax rules, not a particular strange quirk of JS.
"WAT $a .= "World!"; ?"
Including JS - http://mikemcl.github.io/decimal.js/
https://github.com/tc39/proposal-decimal
Seems like it's a long ways away from being available, though.
Way too much abstraction for that to be an argument.
(+ (not (not '())))
The value NIL is not of the expected type NUMBER.
Similarly in Ruby: +!![]
undefined method `+@' for true:TrueClass (NoMethodError)
[Condition of type TYPE-ERROR]
Interestingly, Python does the same thing as JS in this case, even though it is typically quite strongly typed. Edit: not quite the same, as the empty array is converted to False in Python, just as it is in Ruby (in CL, nil/'() IS the canonical false value); but still, Python outputs 0, it doesn't complain like the other two.yep, this is one of those Python weird bits. in Python, booleans are ints, True is 1 and 0 is False. and i don't mean it in a JS-ish way like "they can be converted to...". no, True is the integer value 1. in fact, the type bool is a subtype of int. if you think of types as sets of values, and subtypes as subsets of their parent set, that `book < int` relation suddenly makes a lot of sense ;)
>>> +(not not [])
0
>>> (not not [])
False
>>> 0 == False
True
>>> 1 == True
True
>>> True + True
2
>>> True - False + True * 0.5
1.5
>>> isinstance(False, bool)
True
>>> isinstance(False, int)
True
>>> bool < int
True
so, if you accept that the operation `not []` makes sense to be defined as True (because `bool([])` is False), and that it makes sense for False to be the integer 0, then `+(not not [])` being 0 is just a logical consequence of that :)for the record, i do think it's weird for Python to define bools as ints, and to make all values define a boolean semantics via __bool__().
We consider >70% passing.
The only reason I have learned some of that oddball stuff with JavaScript is because of some job interviews e.g. when I was earlier in my career, I used to Google things like "top 30 questions asked in a JS interview", etc, but I forget it after that until I'm about to look for another job. However I wouldn't do this type of learning any longer, since I wouldn't want to apply for a company asking these types of questions.
In the end at work you should use ES6/TypeScript with linting, proper tests and these cases would never occur.
> that's a floating point problem. Plenty of languages will have the same issue.
yes, many (most) other languages do have the same floating point issues, but that doesn't make them less weird. JS numbers are IEEE floating point numbers, therefore floating point issues/weirdness are also JS issues/weirdness :)
> Calling these things weird is fair enough but I can't help thinking this is code you'd never actually write outside of the context of a "Look how weird JS is!" post.
the verbatim code snippets in particular, yes, you're completely right. but the underlying problems that these snippets exemplify are still there on actual "real" running code. they just look less suspicious on the surface.
> Just be moderately careful about casting things from one data type to another and all these problems go away.
true. but still, these things can happen, and when they happen, they tend to sneakily manifest as weird UI bugs, like rendering "NaN" or "undefined" on the screen (we have all seen those... there's plenty of meme images about them too), instead of noisily breaking with an exception, which is way more noticeable and actionable for us programmers when doing introducing these bugs in the first place hehe.
it's true that they are not the most common kind of bugs, i'll give you that, but when they happen, they can be incredibly frustrating in my experience, because you may only realize after they have been affecting user for a looong time (maybe years), but you just didn't know because JS decided it was a good idea to carry on after one of these nonsense operations like adding an array to a number, giving you nonsense results instead of useful (runtime) type errors.
story time!
i remember an ugly case of this which involved some search filters on a big-ish system. the JS code was quite generic in how it handled filters, and looked fine. for some time, all filters were single-value, as simple strings, but at some point the system started handling multi-value filters, which were encoded as arrays of strings. well, the programmers who implemented the UI for multi-valued filters just tried sending array of strings as filters to the existing filtering system, and it seemed to work correctly in the results it yielded, so they assumed the code was prepared for that too (it looked generic enough), and so they shipped the feature that way.
it was only years later, when i was porting some of that code to typescript, that typescript complained about invalid type operations. i was converting between languages pretty willy-nilly and assuming the existing system worked correctly, so i suspected typescript was being dumb with that type error. but no, it was actually complaining about an actual bug. when passing arrays of strings as filters, the underlying filtering system was at some point coercing those arrays to strings by accident. so ['apples', 'oranges'] became 'apple,oranges'.
the search results were always right when the multi-valued filters had only one value (because ['apples'] coerced to string is 'apples') and kinda right in some naive cases of multi-valued filters (because of how the text search engine worked), which was probably why the original programmers thought it was working correctly and didn't give it much more though or more thorough testing. but the search results were definitely not correct in most non-naive cases of multi-valued filters.
so our users had been affected by this bug of multi-value search filters basically not working for years, and we only discovered it by accident when porting some of the JS code to typescript because typescript was kind enough to tell us about this nonsense operation statically, without having to even run the code. i wish that JS were also kind enough to complain about these nonsense operations, albeit at runtime. it would have made this problem obvious from the get go, and would have prevented the original programmers from shipping a broken feature to thousands of users.
plot twist: although the story may make it seem that i was the competent programmer that figured it all out when porting the code to typescript, in reality "the original programmers" also included me (probably, i don't remember really), just some time before the typescript port :/
It's true that being popular doesn't necessarily make something good. But it's also true that having some flaws discovered later doesn't make something that revolutionized the world and massively uplifted the standard of living of basically everyone in it bad.
While the fact JS can be used to build all these things puts a floor on its quality, that floor is uselessly low.
But that there is a very broad range of successful applications partly relying on JavaScript for their success undercuts the idea that JavaScript is inherently rubbish. Whether it has some subjective "quality" is a conversation best left for art galleries.
I really enjoyed the questions... and even though I am working in TypeScript, I got only 9/25. Precisely because I avoid f---ed up parts of JS, except for trivia games (https://dorey.github.io/JavaScript-Equality-Table/unified/, http://www.jsfuck.com/).
Some parts of code need not to be understood. They need to be blocked by types, tests, and linters (https://p.migdal.pl/2020/03/02/types-tests-typescript.html).
There are a lot of people being born every year. If a 16-18 year old teenager is getting into web development, they were 4-6 when that talk was given in 2012. Sure, that talk still appears here and there, but it's past its prime and you'd have to be in the right place at the right time to run into it.
Crockford's book (JS: The Good Parts) was ubiquitous among JS devs who were coding between 2008 and 2015 (give or take). These days, younger devs have never heard of it. Due to the Seinfeld Effect, if they read it, it would seem trite and obvious not realizing how revolutionary it was at the time.
The parent comments floating to the top here seem to have a common theme of "I knew that already how did the rest of the world not also get the memo at the same exact moment I did?"
Though, I hope that now people learn JS without its worst parts.
In any real codebase, expressions such as `true + ''` should not be understood. They should be eradicated.
> This website is literally about JavaScript. I mean what did you expect, a .NET application? This website is 99.9% poorly optimized and highly questionable JS. And yet, you have JS turned off.
"What, are you going to check if the results are accurate? Go ahead "
But if you open it console while doing the test, it says:
"Hey, stop cheating! "
<script>var answers="I SAID STOP CHEATING! >:( You will get answers and explanations at the end, you dang dastardly dangusson."</script>
I love sites that have easter-eggs in noscript/console. Noscript I can see being annoying (i.e. instead of showing content), but there are some exceptions like this site.
#4, #13, #14, #21 and #24 are floating point problems and are nothing to do with JavaScript.
A bunch of them are just "learn the language" issues:
#2 I only got wrong because I never noticed JS allows trailing commas. Trailing commas are pretty damn common in most modern languages though. The syntax for empty initialisation of arrays is weird (and I'd argue is a mistake), but the behaviour is exactly what you'd expect.
#5 is the comma operator as also seen in C and C++.
#8 is something nobody should get wrong. Three nots in front of a true is false. Of course it is.
#10 is just an octal literal. They're becoming increasingly rare in modern languages, and were never quite as common as hex literals, but they're also not terribly obscure.
#15 is one I'm slightly ashamed I got wrong. It makes no sense to use increment operators on a bare value. This is common to just about all languages that support increment operators (and it's those, if any, that don't agree are in the wrong)
#22 is a slightly surprising counterpoint, but it also makes sense. I don't know of a language that has a NaN literal, and JavaScript is no exception. So that NaN is actually a global that is bound to the floating point value NaN. Because it is a global, it can be incremented (but incrementing NaN is still NaN).
Everything else is a type coercion issue.
#18 and #23 are coercions I really disagree with. Coercing things to a number shouldn't produce NaN. That truly is crazy. Throwing is the only reasonable behaviour here. Ruby is even worse than JS here ("true".to_i produces 0), Python gets it right.
Other than that, the coercions are all fairly make sense, and are consistent with what other languages do. Now, implicit type coercion (especially under the guise of abstract equality) is a terrible idea because it's easy to do it by accident and get nonsensical results, but the coercions themselves are, by and large, pretty sane.
No one used to risk it because IE was inconsistent with what it meant.
Imagine trying to explain to them that this was their fault? Even trying to find the words to explain what happened made me feel bad that their first experience programming was tarnished by a language that will gladly allow you to do nonsense.
Maybe strongly typed compiled languages would be a better fit?
One can argue that if you want to program it might be too much information for starters - but IMO basic types are such a fundamental concept that it would be better to teach that concept early on.
It also clears up a lot of newbie confusion as you cannot assign string to an int by mistake with strongly typed language. Such scenarios are then handled by compiler so newly starting dev has quick feedback on fundamental issues with his code without having to ask people around.
@ozim, I don't believe you are trolling!
JS is an absolutely awful language for beginners; it's a 155mm howitzer pointed at your foot. Although you can get the hang of the basics quickly, AND it runs in the browser (super-convenient unless you're headless), those "basics" that you thought you'd got the hang of turn out to have semantics that make sense, but are not what you expected.
I wrote JS (about 0.2 of my time, I guess) for my job, for about 20 years. I got 18 out of 25 on that test. Now, I think it was actually an easy test; I got a third of my answers wrong because I haven't learned about a third of JS semantics. I'm either not very good, or not as smart as I like to think.
A certain recent colleague of mine would probably have got 25/25; he was cleverer than me, and he was REALLY into Javascript.
Try a Pascal-type language, to learn an imperative-type language. Modula2 is better. Learning Prolog is (I think) purely declarative, so it gives you the view from the other side of the curtain - as it were. Forth is a perfectly-feasible beginner's language; it was the second language I learned.
I can't recommend any OO language for beginners. Not because I think all OO languages are way too difficult, only all the ones I've used. I haven't used Smalltalk.
I haven't used any flavour of Lisp. That language is so pliable that I'm sure there's a flavour that would be good for beginners.
[edit] Actually, I wish I had learned a functional language like Lisp as my starting language; I strongly suspect that would have made me a better programmer throughout my career.
To be fair the primary reason is not really a virtue of Javascript per se, but the fact that it runs in the browser is a large pedagogical gain that no other language can easily match and is hard to overstate. Learning to program is overwhelming for most students, and setting up your environment is a major component of that. Being able to run your code natively in the browser is a huge boon. Even better, having that code immediately be useable in a very real sense boosts motivation significantly. For most students it really is preferable to let students get very comfortable with basic code constructs before having to interact with the underlying system in any meaningful way.
At the language level, Javascript also has some nice affordances that let you treat constructs as simpler than they are in your curriculum and expand on them later. I think the best example of this is `var`. Viewing variables as uncomplicated "buckets" that happily contain any data you want to shove in them is a good starting place when introducing the very first building blocks of something like if/else control flow.
Most languages are built with semi-experienced programmers in mind and are ill-suited for many beginners and actively bad for children. As an easy example: as a developer I like seeing type specifications for numbers, but pedagogically having to introduce the difference between ints and floats on the first day is just empirically confusing for most students. Much better for them to be "magic" until you can properly motivate them to care (which can be just a few weeks in!)
I could write on this topic far longer than I have, but the short of it is that Javascript has done a truly surprising number of things right for a teaching language. I'd still love to remove some of the zany examples here if I could though.
It is like learning how to snowboard, I am now on a level where I can go downhill quite quickly, but I don't have enough experience to not crash if something unexpected pops up.
Learning with `var` is just a bucket seems to be that it is going to cause a lot of frustration and pain later on when person thinks he gets it.
I agree OO is too much for any beginner. Getting started with procedural thinking + basic types is in my opinion a requirement.
I agree nice parts about starting with JS is that basically only things you need is text editor and a browser.
Unfortunately data types will show up one way or the other so there is no need of hiding those. Even in Excel creating formulas one has to understand text vs number and difference between '.' and ',' and how operating system configuration make sometimes '.' to be decimal separator and sometimes ','.
I'd argue that if you don't know about type coercion in JS, you don't really know JS. It's a feature; none of these operators will throw; it's a conscious design choice.
However, that is not to say JavaScript is without footguns. For example, the classic `[1, 2, 3, 10].sort()`.
No one is calling them that except you. The site says it right at the front:
> most of this syntax is probably, and hopefully, not something you use in your daily life.
Your obsession with having to choose Side A or Side B won't let you see this for what it is...
Just showing weird syntax in JS. That's all. Good lord.
However, deciding which language to use for a certain task is a legitimate choice that people to make. When making such decision, people need to analysis the pros and cons of a language. For countless times, I have seen people claiming JavaScript is bad/inconsistent/weird for reasons like `"1" + 1 === "11"` but `"1" - 1 === 0`. All I am saying here is things like these should not weigh too much when deciding whether to use JavaScript.
JS may be weird, but the kind of weird that works well enough and is rather easy to pickup and maintain with reasonable rules in place. When it comes to JS my empirical experience has often been: most of the time, the issue is situated somewhere between the keyboard and the chair.
Javascript got popular because it was here first, so developers used it and became able to work with it. When this happens, javascript had inertia which is impossible to stop. Javascript allows one to deploy anything on any platform with a web browser, without copying files.
Webassembly is great, but it's not easy to build WASM files, the toolchain software used (compilers linkers etc) are not mature (only rust can build to WASM natively), and it requires that all language compile to wasm, so compiler developer need to implement a WASM compile "target", which takes time and is not always possible depending on language (python comes to mind, because of its large library, global interpreter lock, etc).
Also, WASM doesn't have access to the DOM or webGL, meaning that you still need to make JS calls to interact with a webpage.
And you could easily copy and learn from scripts before minification became the norm. And you just have to refresh some page after updating your sources to see the result.
In essence, it's very expensive to risk losing backward compatibility or to make large portions of js software obsolete just to remove some bit of language ambiguity.
It's very frustrating but it's true for all languages out there. Same concept when linus torvalds yells "YOU DON'T BREAK USERSPACE". Backward compatibility almost has its own philosophical chapter on software design.
Also remember how painful it was from switching from python 2 to 3. I guess a solution would be heavy usage of linters, typescript or other compile-to-js solution, but in the end, a lot of developers are just wishing very hard for better solutions.
Personally I am really not willing to become a professional JS dev. I'm too perfectionist and nitpicky to have the courage to suffer so much for such thing. Deep down I know it's a bad choice, but I'm too lazy.
- you have to package your complete runtime in the Wasm binary, this leads to a large binary and memory footprint
- DOM manipulation requires calling Javascript code, and calling JS from WASM is costly
Languages that perform their own memory management (C, C++, Rust etc) will be extremely small. Just one big array of bytes/instructions.
Further, tree shaking is much harder with a dynamic language.
My understanding is that a WebAssembly implementation would usually be smaller than a JavaScript one, assuming you’re using a language without a runtime.
But guys who control web standards resisted for decades to suggestions for other languages, though it was fine to have flash/activex for the same period of time, until Apple killed it for good. Even JS “2.0” (a theoretical better but incompatible version of itself) had no chance, because reasons.
Javascript is okayish generally, but its unusual parts come from the times when it was used as a glue between textual inputs and some~ data structures.
Stroustrups’ phrase is just a stockholm syndrome (in his case self-induced).
Edit: webassembly support is not really required to transpile anything to a browser, see asm.js
I've had the same issues with PHP in the past too, which makes me believe that whosoever created these languages had ease of use as a higher priority than strictness, i.e. it's okay to assign bool, int, string to the same variable.. just get the job done.. okay.
On the other end of the spectrum is a programming language like Pascal (or object Pascal which I used for some time) where I had to write so much boiler-plate code that it made a simple job difficult. Maybe good for big projects but checking if email is valid before form submission, it may be too much boiler-plate code for some people with such traditional languages.
Pure JavaScript is definitely a bit too loose for my liking, but I thought Swift went a bit too far in the other direction.
I worked on a high-profile project for my company under a strict deadline and Swift was a major part in ensuring we delivered a rock-solid implementation in record time.
It's a fun quiz nevertheless, at least for someone like me who is not very proficient in Javascript.
CSS in the console is a nice touch though.
Ty for bringing it to attention. Indeed a cool feature to create emphasis and structure. Haven't bothered with it yet but will consider it. Another cool one is using console.groupCollapsed (as long as you are staying in a callback context).
First time i ever see a form behave like that oO
Every programming language has some weird stuff in it.
If you are writing code like that then you are the problem not the language.
Is this just for the clicks? Is this developer click bait?
What I find sorely lacking is sane syntax for working with arrays. Eyeing Python with envy every time.
And some pattern matching would be nice too.
You can (and sometimes will) run into instances of this without noticing, because while you won't explicitly write
true++
you might do something like x = someFunction()
x++
not realising that someFunction() might return a boolean under some circumstances.Anyone who worked with a sufficiently large JS codebase ran into one of these cases and got unexpected results at some point due to this.
I'll just externalize my embarrassment and say that JS is indeed weird! Another excuse is that I typically try to avoid implicit type conversion except for idiomatic "tricks" such as using !! to convert to a boolean.
Cool website though!
2. One or two of those , questions got me. I don't _hate_ it, but there's a little frustration there. Mostly I just need to know the syntax. If I was writing javascript for money, I imagine it would bite me once or twice, then I'd just memorize the rule.
3 (the biggie) implicit conversion. Oof. seems like a lot of types can be converted to other types, and it's not obvious to me what the precedence hierarchy is. With C I have to look up the edge cases around signed/unsigned, but usually it's just use the next bigger one int -> long. Java does the .toString for everything which has bitten me once or twice. I really don't have a sense of javascript implicit conversion. Some of those seem really subtle.
If you write code like this, you have bigger problems than using JavaScript. For most of the examples, it's like saying "Here's what happens when you plug a fan to a faucet". There's no point really.
JavaScript is weird just like any other language can be weird when you use it in weird ways. And yes, it's certainly one of the weirdest.
If you want to talk about things weird in JavaScript, let's talk about ES modules in Node.js and the Browser, or the number of decisions you have to make to bootstrap a simple web app in JavaScript, and how a language written in 10 days is shipped to billion of devices today.
But, this kind of content, (while well made technically) is always the same one about JavaScript.
x = checkFoo()
y = checkBar()
z = checkBaz()
if (1 === (x + y + z)) {
....
}
It's a rare construction but I have used it intentionally once or twice in the past decade.So you never did something like this:
x = performCalculation(getParam1())
y = performCalculation(getParam2())
sum = x + y
only to discover that "performCalculation()" sometimes returns a boolean instead of a number? JS is a dynamically typed language after all and functions like function f(x) {
if (x >= 0) {
return Math.sqrt(x)
} else {
return false
}
}
are perfectly valid. If you use a 3rd party library that uses return values like this, you might run into such case without realising it.Sure, you won't explicitly write "true + false", but "f(x) + g(x)" is not uncommon and might indeed evaluate to "true + false".
Also, in this case, the function should return NaN so it's not polymorphic.
Sure. But what if that's function comes from a 3rd party component that doesn't have great documentation or relies on another package and thus a behaviour like this simply bubbles up through the call chain?
And don't think this can't happen - NPM in particular notorious for this kind of deep dependencies. Also mixins and monkey patching are a thing in JS, so just importing a module can lead to an unexpected change in behaviour.
The float examples aren’t weird in JS, they’re the same in all languages that use IEEE 754 floats, which specifies that NaN != x, where x is anything.
“This is due to a decision made by the IEEE-754 committee for a few reasons, such as space efficiency and the fact that the function isNaN didn't exist at the time.”
This explanation feels like it’s trying hard to downplay the reasons and make it sound arbitrary and almost whimsical. It doesn’t make sense to allow NaN - NaN = 0 (Note that Infinity - Infinity == NaN), and it’s important that NaNs used as input to computation produce NaNs as output (other than when using boolean tests).
I have been a web dev for about 10 years now and I rarely if ever run into these issues. More likely, I face issues and bugs regarding state and just flaws in the logic rather than issues based on that the language wasn't designed so great.
For sure this can be worrying if you do something really important but at the same time you have a lot of other languages to choose from.
With the advent of linters most of these patterns started being rightfully considered bad form by default.
Currently even innocent stuff like +new Date() (outputs a timestamp) is already something that I've rarely seen make it through review.
JS is goofy, but ES2015 managed to avoid making the same mistakes. Shame it took so long to implement it.
At the moment the bigger problem is the painful transition from whatever module system you had to ES Modules.
Especially browser<->Node.js interoperability suffered massively from this.
Hopefully there will be no further standards in this field.
The scope was just too broad.
I didn't enjoy the quiz, it just reminds me how depressing the base layer of the thing I'm most passionate about in life is. The expression [1,2,3]+[4,5,6] returning what it does is just a bummer, it just sucks. Calling it weird is giving it undue merit, as if it's cute somehow that it's so awful.
> This website is literally about JavaScript. I mean what did you expect, a .NET application? This website is 99.9% poorly optimized and highly questionable JS. And yet, you have JS turned off.
Yes, a website can be about Javascript and still be a collection of documents in web standards, like HTML+CSS, possibly with optional interactive features that could be implemented in Javascript.
I mean, what did you expect? That it is impossible to edit a book on paper about Javascript because obviously paper cannot run Javascript code? Even I have books about Javascript, and they render fine.
const
x = [1, 10, 2],
y = x.sort();
Now: what is the value of x? (Edit: and y?)If the question is about const, const with Arrays/Objects are by reference rather than value, nothing is stopping them from being modified later in the code.
* Except Object.create(null), which throws.
I think perhaps this "makes the most sense" from a language designer/implementor's perspective, but it also gives ample opportunity for WTF moments in actual use.
And that's the first gotcha: sort() is in place.
By the time I saw the answers, I couldn't remember why I guessed which way on each question.
When is the last time you took an in-person test where the proctor told you if you were right/wrong after every question?
You need to grade current knowledge. In fact, I would have removed the multiple choice altogether for this test and had a input(type=text)
EDIT: OK, ok: I get it, it is just a fun website, not the BAR.
> Output: 0
> You answered: I give up
> You answered incorrectly.
No I didn’t.
(Not my fault there were two potentially correct answers!)
The reason is that JS is dynamically typed and thus functions are free to return multiple result types. So you might run into one of these and get unexpected results because at some point in your code one of the functions returned an empty array instead of a number or a boolean, or divided by zero, or...
It's good to at least be aware of these edge cases so you can avoid them.
Biggest problem for JS, from the beginning was people start writing without completely understanding it. Even myself seeing my initial JS code feel embarrassed. Noone understand what is or handle loose typing, prototype, closures, functional concepts like currying before coding. I took almost an year to understand same. Although that time there are hardly my resources like now. But I still see, people directly jumping into JS.
(They’re all empty strings).
The real lesson is that this doesn't necessarily matter, if they're not essential to the code you write most of the time.
Also some of those about floating point math, which is not JS being weird, but rather about needing to know what a float is.
I recently tried to make a simple flask app to sort pictures on my computer. I used file:/// since there were a lot of them.
I was quite unhappy to discover that CORS is quite restrictive...
In a language like Java you have objects and classes at the bottom of the language. How would you add maps to the language? Well, you create a Map class, implement put, get, has methods, etc.
Now imagine a language where you have functions and maps as your basic primitives, but you don't have classes and objects. How do you add objects and classes to the language? You can do something like this: objects are maps, information about their basic class is saved in 'prototype' property, constructor is a function that returns an object, etc. That's how JavaScript works. You can read about this in more details here https://exploringjs.com/impatient-js/ch_proto-chains-classes... , if you keep the basic idea in mind the details are easy to follow.
Works fine in Chrome.
Then we can get into a lot of fun when we get to type systems like SML and Rust where you start to really touch on some power (but can become intimidating). I'm still a bit weary of trying to hop into Haskell.
“11” - 1 = 10
(ノಠ益ಠ)ノ
What would "abc" - "def" mean? Would "abcd" - "bcd" = 0? So it's already an absurd thing to do
Why are we so afraid to call trash "trash"? It's not attacking the creator, but just how can we make progress if things are not perceived as they are?
JavaScript has a bunch of quirks, but it's still great. In the StackOverflow survey, JS reliably ranks highly in the list of "most loved" languages. https://insights.stackoverflow.com/survey/2020#technology-mo...
Node.js is a thing because people liked JS so much that they wanted to use it on the server side. You may think they're all fools, but it's actually pretty nice. I like the latest version of Node better than working in Python (mostly because node_modules are better than virtualenvs, IMO.)
You can use TypeScript to address many of the quirks described in TFA, but you can also reliably avoid them just by never using == and always preferring ===, using String() before using + to concatenate, and using Number() before subtracting.
I basically never encounter situations like the ones depicted in TFA in real code, because I never try to add two arrays or subtract two strings or what have you.
“Admitting like this has a bunch of quirks, but it’s still great.” (not arguing for it though)
Criticism is a driving force behind change to the better. Calling something “great” without pointing out great sides I find… pointless and harmful.
To name a few great points, distinct from other languages:
JS has a pretty shameless object model, where you (usually) have ordered keys, and every key is a property with an {enumerable, writable, configurable, get, set} descriptor. It is much more usable and practical than in almost all other languages. (They do that for “efficiency”, but that’s non-sequitur)
JS has very useful destructuring syntax, which many languages lack of and that leads to assignment bloating and boredom. Also, jit optimizes out temporary objects, so foo({x, y}) / function foo({x, y}) works almost at the same speed as foo(x, y).
JS has a nice Function type, which allows easier metaprogramming and substitution of ‘this’ object. Functions are objects, so you can function foo() {}; foo.x = 1; console.log(foo.name, foo.x).
JS bare objects may have methods and properties: t={_x:1, get_x(){return this._x}, …}
JS doesn’t treat syntactic lists in a last-value-only way, so you can expect foo(…a, …b, c) to work, and to work as intended. E.g. Lua cannot do that, and python (afair) requires you to collect items into a single array (not sure, correct me if I’m wrong).
But what people usually mean by “great” is “it generally works”. I know it is an american thing, but some aspects of js, including those you meet everyday, are really just trashy. It would be nice to not have them at all.
I don't understand what this actually contributes to the discussion beyond it being a tired, beaten-down programming meme.
It's ES3 API is missing a few things, like Object.keys, etc. I still happily code for browsers in ES5 with a few polyfill functions.
JS works fine for it's core competency: manipulating DOM elements and local data representations.
It's not JS's fault that the browser is the way it is and that HTTP is the way it is and that using a remote directory "browsing" protocol via a specialized file browser that renders hypertext isn't a 2-way bound GUI framework...
There could definitely be improvements, but trash seems to be taking things too far.
As an experienced Python programmer learning JavaScript, this isn’t true for me yet but I hope it becomes true. I think JS will be much more useful to me than Python.
> Which is fairly easily banned with a linter.
Any particular recommendations?
https://github.com/standard/eslint-config-standard
I would start with that and tweak what you don't like
TypeScript.
It's odd, there are developers who make the language they work part of their identity and see attacks on that language as attacks on their identity.
I don't get it, but it is what it is. If you call javascript trash, you will offend people.
JS is a great programming language with a lot of quirks and footguns, most of them easily avoidable by enforcing good coding styles, using easily available and widespread tooling.
And most language have linters or compilers that issue coding-style related warnings, including C, so this is not specific to Javascript.
Basically, it was meant to be a dialect of Scheme, but with map and array data structures at the bottom, as opposed to lists (hello, Clojure!). Suddenly, the last-minute decision was made to add Java-like syntax and OOP to this language and rebrand it as 'JavaScript'. Brendan Eich did the best he could in the limited timeframe to implement this. Then in 2015 'design-by-committee' approach was accepted and horrible feature creep started. We can then investigate every feature and how it pushed individual company's agendas while compromising the integrity and vision of the language, but that's a typical design-by-committee story.
> how can we make progress if things are not perceived as they are?
People aren't blind to JavaScript's failings. The evolution of JavaScript has been a mix of adding new features and fixing what they got wrong before, e.g. the way it has two different isNaN functions. [0]
[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
Python has an interesting C FFI interface, among other approaches, that can at least allow CPU and memory-bound tasks to be accomplished inside of a native code module. You see a lot of domain specific work in Python due to these approaches, cython, etc..
Crystal lang is what I would urge all Ruby devs to look at. (an LLVM-based compiled language that has HTTP in it's standard library, for one thing...I'd pit it against Go, for example.)
As I see large front-end teams frequently pushing JS from a typescript base lately, I'm not sure that even the JS community supports "everything JS" anymore. At the point one does backend Node.js work with typescript or other more rigidly typed systems, I have to wonder if targeting V8 is really the desired option any more, and if the programmer would not be better off switching to a high-performance and feature-complete typed backend language system. (the single-threaded JS execution model being a needless constraint at this point, for example. It's great as an event loop)
I have no idea how you could come up with this sentence after this brilliant display of what an insanely stupid language JavaScript is.
C, C++, Perl (oh, Perl!) allow you to do weird things, too, so do lots and lots of other languages.
A certain knowledge of this weirdness is very helpful, though, in avoiding it.