Hacker News new | ask | show | jobs
by parentheses 1198 days ago
The way JS/TS change _feels_ a lot more haphazard.

For example why introduce a new method to support negative indexing. Supporting `array[-1]` instead of `array.at(-1)` would mean one less thing to remember.

Many of the changes make the language feel like a hodge podge made from parts of other languages. This lack of cohesion is IMO what makes upgrading the language always feel like moved cheese.

1 comments

Because your example is a breaking change, and breaking changes are hard to make in a runtime that needs to reasonably support two decades worth of web content.

For example, if you have a `binarySearch` function that returns -1 if an element isn't found, a developer might do something. `const result = arr[index]; if (result !== undefined) { ... }`. This would then start returning the last element instead of undefined at that index.

We already have things like "use strict", because of backwards compatibility. Following the same idea, we could have something like "use ES2023" or something along those lines. Issue with JavaScript is that browsers have in-flux implementations of new features (as browser parent companies see it fit for their usage), and there's no cohesive point in time release process. I think "living" standards, are part of the reason why the web stack is so jumbled.

But what do I care, whatever mess and complexity arises from these "good enough" implementations is left for the generation after us to deal with :)

In some sense, the messiness of the living standard is also what enables large-scale web archival.

A lot of old code in other languages may be hard or impossible to compile and run without significant work.

Exactly right. `arr[-1]` means `arr["-1"]` and already does something.
It is also a breaking change to use new syntax and functions since old browser does not support new features. In this perspective `arr[-1]` seems a fair breaking change.
No, because changing browsers to interpret `arr[-1]` as `arr[arr.length - 1]` breaks existing sites that expect `arr[-1]` to be interpreted as `arr['-1']`: That is, the value stored on object `arr` at key name '-1'.

Changing browsers to interpret `arr.get(-1)` as `arr[arr.length - 1]` doesn't affect any old code using `arr[-1]`.

It's not about supporting old browsers. It's about supporting old code.

I think you're confusing your application with the language itself.

Adding new syntax and functions to the language is not a breaking change. Old code will continue to work.

If you start using these new features in your application, and it no longer works on old browsers, then sure that's a breaking change. But that's a choice for you to make. The language is still backwards compatible.

`indexes[haystack.indexOf(needle)] = true`

There's a valid example of code that would be broken (`indexOf` returns `-1` as "not found"). Is it a good way of solving whatever the author was trying to do? Probably not, especially now that sets exist. Is it code you might conceivably find on hunreds of sites across the past decades of the world wide web? You bet.

Yes, we could introduce another "use strict". But we only just got rid of the one via ESM (which enforces strict mode). That was a one-off hacky solution to a hard problem coming off the end of a failed major version release of the language (look up ECMAScript 4 if you get a chance). We don't want to see a repeat of that.

Why is there still no simple way of handling changes like this?

Surely there should be a simple way to have a header in each file with the language version, and then the file will be interpreted as that version?

All of this was hashed out during the "Harmony"[1] days. Versioned-JS was of course one possible future. Maybe even still is. But the prevailing decision coming out around that time and leading to ES5 and ES2015: We'll add "use strict" as a single-point-in-time breaking opt-in upgrade to fix a lot of the common problems, but let's otherwise stick to "One JavaScript"[2].

You may find [2] and [3] especially enlightening to understanding this thinking, and any other discussions from ES Discuss on the topic if you fell like digging into history.

[1] https://johnresig.com/blog/ecmascript-harmony/

[2] https://2ality.com/2014/12/one-javascript.html

[3] https://esdiscuss.org/topic/es6-doesn-t-need-opt-in

Well, "use stricter" and "use strictest" are still available...
!important
Maybe this is simple in implementation, but it's definitely not simple in developer experience.

You grab some code in one of your old projects for implementing a binary search. Can you copy-paste it into a new project that targets a newer language version?

The question isn't as simple as "does it have syntax errors", because we're talking about changing semantics here. Given a set of semantic changes and a piece of code, figuring out (either as a human or a computer) whether the observable characteristics of that code have changed is somewhere between vexing and impossible. It's entirely possible, for example, that your code encounters changed semantics, but not in a way that changes the actual behavior of the code.

In this world it just becomes very, very difficult to reason about extremely common operations; it'd be a constant source of frustration. There's a good reason you rarely see languages versioning their behavior in impactful ways.

> Why is there still no simple way of handling changes like this?

This is nothing JS specific. Breaking changes are breaking changes. If you can, don't introduce them.

> simple way to have a header in each file with the language version

One special aspect that differentiates JS from other languages:

It's both a language AND a universal runtime. A lot of JS that's executed is not JS that's written by humans but generated by a compiler/transpiler.

So adding a layer of header versioning is not a big win in terms of developer experience: It would anyways be the deployment toolchain that's responsible to deal with such a versioning scheme. It would ideally be invisible to the developer.

To be pedantic adding an at function is a breaking change too.
Not really.

You can add a polyfill to check if `Array.at()` exists, and if it doesn't, create a function that does the same thing and add it to the `Array` object, so now all `Array.at()` code works as expected.

Then once every environment you target supports `Array.at()` by default, you can remove the polyfill to reduce the size of your code.

    if (array.at) {
      explode();
    }
Adding the at function would break the above code which depends on array.at being undefined.
They test a lot of websites before introducing new methods. Something somewhere may break but it's very unlikely and this pragmatic approach allows progress.

This is also why the language got Array.prorotype.flat instead of flatten (flatten was breaking an old version of a popular library called Mootools): https://developer.chrome.com/blog/smooshgate/

And extending javascript's built-in objects has been considered bad practice since at least 2007.

Before that point, browser environments were so different that you needed to write code per-browser. Those theoretical concerns didn't really matter since in-practice you were essentially coding the same app in different scripting languages.

> extending javascript's built-in objects has been considered bad practice since at least 2007

Totally. It's just extending the prototype that causes the problem though, not extending from (class myclass extends array). This causes a lot of confusion among new js devs so I underline this on every opportunity.