Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Nit-pick: Javascript is not simple; I personally think it has among the most complex language semantics out of all commonly used languages.


Javascript would be a simple hybrid of object oriented and functional principles if backward compatibility with old hacks and "robust" use for web page scripting didn't require a host of redundant features, syntactic bizarre special cases and and evil semantic choices: -- comments, iterating objects and arrays, the absurd equality operators and type conversions, and so on.


Prototypal inheritance can be pretty awkward and complicated. It’s evident in that few young JS devs ever use it, and many don’t even know about it!


I'm an old JS dev, having been around from its beginning, and I avoid JS's OOP system of prototypes too (along with the class syntax) and stick to only static closures in object literals. VueJS and TypeScript are also mostly written in that style, so I'm in good company. I'm tickled pink that we're at the point where a dev actually _can_ forget that the OOP system exists, because its a horror show. I learned OOP on a prototype-based system (LambdaMOO) so it's not like it's foreign to me, JS's system is just so full of warts and wats it's not funny.

It's scary how good TS is at inferring the type, it'll even infer an instance of a class from a literal, so the interop with class-based code is almost seamless (almost: just don't try destructuring a "real" object or re-binding `this` on a static closure)


Can you give an example outside of the weirdness caused by automaticy converting types? (Or 'with', which is kind-of in purgatory now)

I have considered a non backwards compatible JavaScript descendant to clean things up. It would be interesting to hear what you consider to be problems.


Empty slots in arrays are weird.

    >>> let xs = [,'foo']
    <<< undefined
    >>> xs
    <<< Array [ <1 empty slot>, "foo" ]
    >>> xs.map((x, index) => index)
    <<< Array [ <1 empty slot>, 1 ]
    >>> for (let x of xs) console.log(x);
    undefined
    foo
    <<< undefined
    >>> for (let x in xs) console.log(x);
    1
    <<< undefined
    >>> xs.length
    <<< 2 
    >>> delete xs[1]
    <<< true
    >>> xs.length
    <<< 2
    >>> xs
    <<< Array [ <2 empty slots> ]
Empty slots are skipped in map() and for-in, but not in for-of and the new array from map() will have the same empty slots. delete will change a slot to empty, it won't change the length of the array.

Still, much saner than PHP arrays.


> Still, much saner than PHP arrays.

PHP basically has arrays and maps merged into one. Basically like Lua does.

It is pretty handy. Not sure what you find insane about it.

Of course the functions for arrays in the standard library are consistently inconsistent but that is just a general PHP thing. Isn't a big deal when you use an IDE.


> Not sure what you find insane about it

How about array_filter() returning an associative array, because it doesn't renumber the indexes? You need to run it through array_values() after. You run into this all the time when converting to json.

For something more obscure but equally infuriating, how about using iterator_to_array() on anything that uses `yield from`? Everything yielded that way will overwrite previously yielded values, unless you pass a magic boolean parameter that of course defaults to doing the wrong thing (PHP is chock full of those). OFC it's because of the behavior of array keys.

How about when you do want associative array semantics and use a string index that consists entirely of numeric digits? It gets cast into an int, always, and you cannot control that. This is super "fun" when you run array_keys on your associative array you only used string keys on, and naïvely think it will return only strings. Crash at runtime if your function was declared to just take a string.

There are so many more WTFs, these were just off the top of my head from issues I've encountered recently. I make my living with PHP, but you'll never see me defending arrays. Though at least they're zero-based As The Deity Intended It.


Off the top of my head:

- There's both undefined and null

- There's three ways to declare variable

- typeof vs instanceof

- for in vs for of

- the way `this` works

- semantics of functions called with the wrong number of args

There's far more that I can't immediately recall.


A lot of that is what I had in mind. Things like for...of and let fix glaring problems by introducing new things instead of changing to maintain backwards compatibility.

A lot of the fixes are just removing the old broken way of doing things.

I'd still probably change 'for x of ...' to implicitly be 'const x' and require let if you are doing shenanigans with changing the values.

I am surprisingly ok with null and undefined. Null means not a thing, undefined means there is not even the notion of the thing.

I'm pretty sure there are more than three ways to declare a variable.

    x=5
    let x=5
    var x=5
    window.x=5
    window["x"]=5   //arguably the same as above.
    Object.defineProperty(window,"x",{value:5}) 
    Object.assign(window,{x:5})
at least


> Null means not a thing, undefined means there is not even the notion of the thing.

If only "undefined" was consistently the same as lack of variable / collection entry, it would be understandable. But as things are, {x: 1, y: undefined} is not the same as {x: 1} in many ways - indeed, so much so that TS distinguishes between these two cases in its type system.


Many of those are setting properties, not variables.

A variable reference can lookup properties on the global object, or other objects with ‘with’


> There's both undefined and null

To make matters even worse, there's a difference between setting an object property to undefined as opposed to omitting it. So one could say there are two different "undefined"s.


And just for fun, `typeof null` returns `object`, which is an acknowledged bug they'll never fix because compatibility.


> three ways to declare a variable

FWIW Python has three (default, nonlocal, and global).

And many languages have two.


prototypes vs class


Javacripts has lot of low-level functions like [[Get]], [[Call]], [[ToPrimitive]] which can be redefined. If you believe that JS is easy, do you remember how these low-level functions work? Also, JS has prototypes.


Simplicity in high-level: Lua, low-level: Zig (combined in one (complex): Rust?)




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

Search: