loading...

Is JavaScript the most confusing programming language?

Ilona Codes on August 30, 2019

People often say that JS (and implicitly NodeJS) is confusing because of its magic power. Moreover, JS landscape changes over time and changes fast... [Read Full]
markdown guide
 

I think it's dishonest and counterproductive to pretend that all languages are equal. TypeScript is in fact born out of the problematic reality that writing JavaScript on large teams is more expensive than it needs to be.

JavaScript is very confusing, to the extent that "confusing" is the opposite of "easy to predict" and "understanding it is not complex".

For a tiny selection of the confusing complexity of JavaScript:

{var x = 1; {var x = 2;} console.log(x)} is 2 but {let x = 1; {let x = 2;} console.log(x)} makes 1.

doThing(arg, (x) => { this.receive(x); }); will generally work when doThing(arg, function(x) { this.receive(x); }); will generally not.

[1, 2, 3, 10, 20, 30].sort() is [1, 10, 2, 20, 3, 30].
["x", "y", "z", "a", "b", "c"].sort((a, b) => a - b) doesn't change the order.

"slice" in [] but for (let p in []) {console.log(p);} prints nothing.

"𝕋𝕋".length is 4 but Array.from("𝕋𝕋").length is 2

{[{}]: 10}["[object Object]"] is 10 even though that obviously doesn't make sense

(a => a).length is 1, and Array.from((a, b) => a + b) is [undefined, undefined].

arr.push, arr.pop, arr.splice don't return the array, but modify it; arr.sort and arr.reverse modify and return the array; arr.concat, arr.slice, arr.map don't modify the array.

0 == "0" and 0 == "" but "" != "0".

Certainly all of these have (relatively straightforward) explanations. However, each of these represents a piece of "trivia" that you have to know in order to actually understand JavaScript code in the wild; they are each some of the many hurdles that a programmer has to (repeatedly) overcome in order to not be confused by JavaScript. This is also, obviously, only a selection of a few prominent examples of JavaScript-generated confusion; knowing these does not mean you won't be confused by JavaScript.

Still, JavaScript is definitely not the most confusing language. And I agree, it is getting a lot better (especially as co-existing tools like the TypeScript compiler make writing plain-old-JavaScript easier). (Unfortunately, as the above list somewhat illustrates, this also makes JavaScript more complex as "legacy" features coexist alongside "modern" features with subtly different behaviors).

As someone who tries to keep up with programming language theory, I am afraid of complacency with programming tools. Where we are today is obviously nowhere near the "apex" of programming tools (after all, programming languages themselves are only about 60 years old at this point) which means there should be lots to improve upon. Recognizing we have come far is important, but it is also very important to not ignore areas that we can still improve.

(The above is not unique to JavaScript, though I do think JavaScript is one the more-confusion-inducing end of the spectrum. C is probably the contender for the language most are often called "good enough" despite well-identified areas were it deserves improvement).

 

To be fair, the first few examples don't line up specifically because the old language feature was not obvious and new languages features were added to correct this (e.g. variable scoping with let vs var). Also, the arrow function vs normal function are explicitly different because it's actually useful to have both. Sometimes you want to stay in the current scope, sometimes you want an isolated scope.

There are many things JS does weird, but several of these examples are attempts to correct things. With the variable example, it's very recommended to just not use var ever and just use const or let. But var must be kept around for compatibility.

That said, TS is absolutely trying to make things even better and helping remove a full class of errors that compiled language devs haven't had to think about for decades.

 

JavaScript is not confusing. It's confusing for people who didn't read specification, don't understand type coersions and how weak typing works, don't understand closures, etc. Then, of course, you can give an example of:

0 == "0"
0 == ""
"" != "0"

But actually there is nothing confusing here. JavaScript uses number coersion when comparing things of different types, but "" and "0" are both strings so JavaScript just compares them straightway.

It might be daunting to know those things, but it's certainly not confusing when you know them!

Furthermore, you shouldn't even use code like that in production in the first place.

Talking about this weirdness:

{[{}]: 10}["[object Object]"]

It does make sense. JavaScript is not a human language, it's a programming languge with strict rules and syntax and, in my opinion, a developer should think in those rules. Here you create a new object defining ES6 computed property as an empty object. In JavaScript object keys can only be strings, so the method toString() is being called on {} which by default produces [object Object] string. So basically you are doing this:

const object = {};
const weird = {
  [{}]: 10
}
console.log(weird[object.toString()]); // 10

Again, the fact that it's possible to write code like this doesn't mean you should do it. But it is great to understand how the language works! So it shouldn't be confusing for a developer.

Of course, that's just my opinion on that. I love JavaScript ❤️

UPD: I actually got inspired by this thread and wrote a little guide about JS type conversions:

 

As I mention, all of these behaviors have fairly concise explanations. However, having to know dozens of bizarre edge-cases that do come up in real code reduces the space you have in your head for thinking about the important problems in front of you (and instead you have to spend time thinking about language trivia).

These complexities are not inherent to implicit type coercion, dynamic typing, closures, etc. Lua has all of these things, and none of the gotchas that JavaScript has.

For example, in Lua, tables are simply mappings from objects to keys. == is the same thing as what determines if two keys map to the same value (so, e.g., {[{}] = true} actually does do what it says it does; 1 == "1" is false, t[1] isn't the same thing as t["1"]; but you can write 1 + "1" == 2 yet 1 .. "1" == "11").

The fact is, that many of JavaScript's complexities/gotchas/points of confusion are unnecessary. The fact that they can be overcome is secondary to the fact that you have to overcome them: if JavaScript were designed slightly differently, you would not have to worry about them (and this can in most cases be done without sacrificing any linguistic power and convenience)

 

The actually confusing part here is trying to understand, why did they even design a language this way, and why people still insist on using it.

Weak typing does not necessarily mandate type coersion. There's a lot of dynamically typed languages that make sense, that did not opt out for insane rules.

 

Like @jordan explained, Javascript is weird. But examples like let and var is an attempt to solve this not another weirdness. I had one of my most disturbing bugs recently, I completely forgot typeOf NaN === number. That is literally saying 'not a number` is a number.

But just like the English language understanding this weirdness is the only way you can call yourself a fluent speaker of the language. In English you could say 'Giver her her book', but never "Give him him book". "How are you" is supposed to be for a singular person, but "are" in every other situation is plural. We just accept this "bug" and appreciate the more expressive part of the language.

 

I understand the point you made regarding English language, but comparing shortcomings of English language with a programming language is like comparing human brain with AI.

 

Having worked in many languages, I can confirm that JavaScript was the most confusing one for me. I think what makes JavaScript hard is that it uses concepts that look very similar to common patterns on the surface, but work totally different underneath. Favorite examples are this and the prototype-chain. I'd make a bet that a great majority of devs that do JavaScript only occasionally get their code working by accident only, and not because they understand how and when their this is bound.
This is more difficult to master than learning new concepts from scratch, because you have to unlearn things that you might have learned from other languages before.

 

good developers get their code working intentionally by using tests ;)

 

Good languages do not force you to write tons of boilerplate tautological tests to merely get things working at all. The best languages do not even need any tests whatsoever.

Sorry, there is no need to write any "boilerplate tautological" tests in any programming language.

Only write tests for behavior that the business and user needs. No real need to write tests for mistyped inputs at the unit level, because at the integration and e2e level you’re going to prove that nothing in your application is passing these mistyped inputs, only by writing tests that matter to business and user.

The best languages do not even need any tests whatsoever.

Just read this. Sorry, that is just not professional.

Tests are the best way (known today) to make sure that the quality of the software is high.

Any of the advanced type-contract-self-proof things (like in Idris) will still require the developer to write tests at the compile-time level to ensure that the contract doesn't have any mistakes in it. Essentially, in these languages the type definition becomes a programming language of its own, that can have bugs, and need tests.

In theory - yes, of course.

In practice - nope, most of the tests for the primitive dynamically typed labguages are nothing but a poor men substitute for a type system.

Excuse me, I’ve worked with various JS-based projects on both front-end and back-end in different teams. We all did TDD and ATDD, and we’ve never written these "type-system-substitute" tests.

Instead, we’ve tested only behavior from the user perspective.

In these codebases, I’m pretty sure, there are few functions here and there, that would behave incorrectly if you were to call them with e.g. a number instead of a string argument. But that is not a problem because nobody does that, because there are higher-level acceptance tests (e2e + integration) that ensure that behavior is consistent.

So the "in theory, but in practice" argument kind of doesn’t stand. At least not in my view of the world and experience.

I'm sure that tests have little to do with static or dynamic typing, or statically proven languages. Every corner case or circumstance that your function can be called in should be tested. It is true, however, that in dynamic languages there are more circumstances under which you function can be called. I don't find it a significant difference, though.

As of statically proven languages, the only difference is that your tests don't need to run - but they need to exist.

Having said that, after working with C++ for 15+ years, I now value dynamic typing for it will not break your whole build if some detail does not work correctly or is not yet implemented - especially the last part is valuable, IMHO. You can write the code that calls your function before having written it at all, and still execute the rest of your tests. Feels good for gradually turning tests green.

(The last part of my comment is still a detail and personal preference, though!!)

Every corner case or circumstance that your function can be called in should be tested

I agree with this ONLY if we’re talking about a library that is exposed to any user out there potentially.

If we’re talking about the application code where the team has full ownership of the full stack (as it should be), then there is no need to be testing the function how you do not expect to be called (but it could be potentially).

That’s because you’re not writing a function in isolation, it is part of one or multiple code paths of a bigger size. Potentially several layers of architecture, and if e.g. your business domain code is calling this function only with strings (and it would be absolutely non-reasonable to call it with something else), why should you spend your time and time of everyone else in the future (when running test suite) on testing what will happen if you pass a number (or anything else)?

When you have higher-level tests, that check what happens when multiple components and layers are integrated within various user/business acceptance scenarios, you’re going to confirm that function indeed is called properly at all occasions (implicitly).


Of course, if you don’t trust your team members to do the right thing, then you should work on that trust issue, find its root cause(s) and work through fixing it. Please, don’t fix people problems with technological complexity.

You're totally right. The statement came out a bit too pedantic, and yes, I was thinking more of code used by a wider audience. Because, from my usage experience of libraries in dynamic languages, I've learned that it is very frustrating when you accidentally passed in the wrong type, just to get seemingly random error messages four calls down the stack. And that partly confirms @combinatorylogic 's statement "most of the tests for the primitive dynamically typed languages are nothing but a poor men substitute for a type system.", although, as I said, I don't think that the tests that do substitute a type system are too much of a burden.
Again, I'm talking about widely used code here, not your team's shared libraries where you can tap the original author on the shoulder to get help with it if it spills out gibberish error messages.

You're doing it the right way then, good! I'd always take integration tests vs. any amount of unit tests. The problem is with all the people who perceive weak typing as an invitation to write tons of pointless unit tests, I'm sure you've seen it happening a lot.

 

99% of the people I hear complaining about JavaScript at work are expecting the language to work the same as other OOP languages they're used to using. Their complaints are drawn because of their inexperience with the language and what they expect it to do based off of past experience, without actually taking the time to understand it. It's pretty rare for me to go through a week without having to explain to someone why a certain amount of code I wrote works. If they took the time to actually learn JS without making assumptions about it, I think they'd be pleasantly surprised with the elegance they can achieve utilizing it.

 

You're saying that someone who drives an automatic car with a modern engine and electronic assistance should accept that he has to learn how to drive a manual car and drive without assistance just because a language is hyped. (I'm a Front-end Developer)

 

No, he's saying they should learn to do so because they are colleagues that have to share code and knowledge with each other. Why would you blame 'hype' when using JavaScript is almost completely inherent to front-end development?

 

I think it's a whole lot simpler and more elegant with es6. Though certainly, js does still have a lot of weirdness going on having one step in the functional camp and one step in the object oriented camp.

I can say that I really disliked js pre-es6, but now I'm fairly content with it. Not to say I don't have some gripes, but overall it's not a terrible language right now in it's current state.

 

Agreed... I started learning JS right around the time that ES6 started to emerge, and I think its made a huge difference.

I struggle with trying to figure out where it is between functional and object-oriented, and I find myself picking different paths for different use-cases. It's nice to have the flexibility, but it's hard to find a route to consistency.

 

IMHO JS is not just confusing, but it is deeply flawed at the design and philosophical level.

Maybe the biggest flaw (that many consider a feature) is the insistence to doing automatic type conversion, even in context where it makes no sense. (BTW, I program in Ada where no automatic type conversion is done and I like it in this way; it saved me from many bugs).

I mean, an expression like 1 + '1' that is, adding a string and a number, makes no sense at all; most probably is not something the developer wanted, but the consequence of an error. Moreover, what are you going to do? Convert the integer in string or the other way around? What about '1'-1 or 'b'+3? In this case the sensible thing to do is to raise an error. There is one thing worst than a program that stops because of an error: a program with an error that continue nevertheless.

Automatic type conversion also gives rise to one of the worst behavior of JS (still IMHO): equality operator == is not transitive (I suspect that the loss of transitivity can cause several algorithm to break). See for example,

stackoverflow.com/questions/544715...

Along the same lines, but with more humor, a classic:

destroyallsoftware.com/talks/wat

 
 

The problem with Javascript is that precisely that freedom has coursed its grow from the beginning by mean of people that try to own it. I remember the battle of Netscape and Microsoft.

The industry is somewhat complicated, the freedom and competence provides more dynamic but the standardization is needed at some point in order to build at the next level without waste of resources.

Think what would happen if all the telecommunication companies transmit with different types of sockets, cables, protocols, etc.

It might be that JS is profiting from his own hype. Many companies, developers, etc.. are profiting using their creativity to built the frameworks they want to be imposed. Think of it as the Dot-com Bubble of scripting languages.

Someday it will stop, ES or another standard will be taken and the rest of invested resources in that redundant competency would be discarded.

In one of the last attacks to OOP calling it a trillion dollar disaster one could note an attempt to devalue the professionals, theory and technology-stacks that built most of the industrial-strength software that we use everyday.

But I bet that some day we will find that the trillion dollar disaster is JS. Let us hope that it ends at least with a good tech result.

 

JavaScript before ES6 maybe, but now with the new features and syntactic sugar added on top of static type analysis with TypeScript, JavaScript has proven to be a much better language currently for Full Stack development. Only something like the behavior of the this keyword may be confusing for newbies.

This maybe an unpopular opinion but according to me, prototypal inheritance is the best of both worlds. And composition can be easily created in JS which is awesome.

 

Confusing is also that prototypical inheritance is rarely taught, compared to class based inheritance.

 

Don't blame the language, just read the spec 😁

 
 

Language aside (others covered the topic), it's good to discuss the userland. Npm packages are a chaotic mess, hobbyist programs written by semi professionals or professionals during their weekend free time, often abandoned and unmaintained. When you try to get help or support, people will tell you "it's OSS what did you expect" and you'll spend days and days to solve issues diving into code you didn't writ desperately looking for help in discord, github issue trackers, stack overflow etc.

 

You think javascript is confusing? Try Ook! Granted, it's a parody language, but it contains all of brainFuck's eight language commands - making it a legitimate Turing complete.

It contains only three reserved words: "Ook.", "Ook?" and "Ook!" which can be combined in eight different pairs to cover all of BF's commands.

Hello world in Ook!:

Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook.
Ook! Ook. Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook?
Ook! Ook! Ook? Ook! Ook? Ook. Ook. Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook. Ook! Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook.
Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook! Ook? Ook? Ook. Ook. Ook.
Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook. Ook? Ook! Ook! Ook? Ook! Ook? Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.
Ook? Ook. Ook? Ook. Ook? Ook. Ook? Ook. Ook! Ook. Ook. Ook. Ook. Ook. Ook. Ook.
Ook! Ook. Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook.
Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook! Ook!
Ook! Ook. Ook. Ook? Ook. Ook? Ook. Ook. Ook! Ook.
 

The obvious answer is no: there are languages that are deliberately written to be confusing and succeed very well; e.g. Brainfuck.

TBH I don't think this type of headline/question is particularly constructive. It reinforces the (misconceived) idea that JS is somehow more confusing than other languages. Any programming language has quirks that may initially confuse beginners or those moving from another language. Features that some see as strengths, others see as weaknesses. Despite often spurious examples of 'confusing' code that no-one would ever write in practice; the 'issues' that might catch out inexperienced JS developers can be learnt relatively quickly.

Yes, frameworks and ecosystem are changing fast; but ultimately the foundations of JS haven't changed. If you understand those you shouldn't have any problems keeping up. One really good place to get a better understanding of the language is the You don't know JS books.

 
 

Have you ever written c++? Undefined behaviour is very confusing.

code of conduct - report abuse