It has 3 ways to declare functions, multiple variations on arrow functions syntax, a weird prototyping inheritance system, objects you can create out of "new" on functions, object literals that can act an pseudo-classes, classes, decorators, for-i loop + maps + filter + for-in loop (with hasOwn) + forEach, async / await + promises and an invisible but always-on event loop, objects proxies, counter-intuitive array and mapping manipulations, lots of different ways to create said arrays and mappings, very rich destructuring, so many weirdnesses on parameter handling, multiple ways to do imports that don't work in all contexts, exports, string concatenation + string interpolation, no integer (but NaN), a "strict mode", two versions of comparison operators, a dangerous "with" keyword, undefined vs null, generators, sparse arrays, sets...
It also has complex rules for:
- scoping (plus global variables by default and hoisting)
- "this" values (and manual binding)
- type coercion (destroying commutativity!)
- semi-column automatic insertion
- "typeof" resolution
On top of that, you execute it in various different implementations and contexts: several browser engines and nodejs at least, with or without the DOM, in or out web workers, and potentially with WASM.
There are various versions of the ECMA standard that changes the features you have access to, unless you use a transpiler. But we don't even touch the ecosystem since it's about the language. There would be too much to say anyway.
There are only two reasons to believe JS is simple: you know too much about it, or you don't know enough.