https://webreflection.medium.com/js-classes-are-not-just-syntactic-sugar-28690fedf078

After reading yet another blog post about JS classes being “just sugar for prototypal inheritance”, I’ve decided to write this post to help clarifying, one more time, why this statement is misleading; a post that hopefully explains what’s the difference and why it matters to understand it.
Using "use strict" directive in ES5 won’t forbid constructors to be called without new keyword.
// ES5
function Test() { "use strict"; }
Test.call({}); // it's OK// ES6+
class Test {}
Test.call({}); // it throws
The reason is that modern classes have a new.target concept otherwise impossible to replicate in ES5, without using transpilers that simulate such behavior. Transpiler must use an instanceof check too, resulting in slower, bloated, code.
Despite my personal attempts since 2004 to subclass Arrays and others, it’s not really possible to extend builtins in ES5 in a useful, or meaningful, way.
// ES5 epic fail
function List() { "use strict"; }
List.prototype = Object.create(Array.prototype);var list = new List;
list.push(1, 2, 3);JSON.stringify(list);
// {"0":1,"1":2,"2":3,"length":3}list.slice(0) instanceof List; // false
Let’s ignore the fact I am not even using Array.apply(this, arguments) in the constructor, as that also will fail expectations, an ES5 Array extend is awkward no matter how you look at it, and so is every other builtin, including String or others.
// ES5 epic fail v2
function Text(value) {
"use strict";
String.call(this, value);
}new Text("does this work?");
// nope, it doesn't ... no way it can.
OK, I hear you, “who’s gonna need to extend String mate?”, and you’re right: you might not need to do so, but the point here is that it’s impossible to do it with ES5, or better: the prototypal inheritance cannot do that, JS classes can.
In case you’re wondering “how come list.slice(0) is not instance of List?”, the answer is Symbol.species.
// ES6+
class List extends Array {}(new List).slice(0) instanceof List; // true
[].slice.call(new List) instanceof List; // true
Accordingly, unless you are guarding every single method to return the initial kind of instance one would expect through all Array methods, ES5 here is just painful, unreliable, fragile, and not designed to work with species.
That’s it: JS classes are much better at preserving expectations than ES5.