3

If they are just semantic sugar how can I get the same result of the following es6 scripts in es5?

class MyFunc extends Function{}
new MyFunc('alert("hi guys")')()

and

class MyArr extends Array{}
var myarr = new MyArr(1,2,3,4)
myarr[2] = "a value"
myarr.push("an other value")
Charlie Fish
  • 18,491
  • 19
  • 86
  • 179
asdru
  • 1,147
  • 10
  • 19
  • 2
    Well yes and no. You can't extend Array in ES5 the way you can in ES6. This doesn't really have to do with `class` syntax, but rather with making the Array object subclassable in ES6 (its magic `.length` property causes subclassing problems in ES5). But, you can extend other normal objects just fine in ES5 without the `class` syntax. We were using full-fledged inheritance just fine in ES5, long before the `class` syntax came along. It isn't as pretty to write the ES5 declarations, but works the same once declared. – jfriend00 Dec 30 '17 at 18:50
  • Not sure whether this should be closed as a duplicate of [are es6 classes just syntactic sugar for the prototypal pattern in javascript?](https://stackoverflow.com/q/36419713/1048572), which doesn't have fitting answers and doesn't mention your specific case. – Bergi Dec 30 '17 at 18:59

3 Answers3

4

No, they are only mostly syntactic sugar. They can do all the things that the class pattern did in ES5, but also more than that.

The details of how objects are instantiated, especially in subclasses, was overhauled, and now allows to subclass the builtins like Function and Array as in your question. This was not possible in ES5. For details, have a look at What is "new.target"?, What does super() actually do in constructor function? and What do subclass constructor objects inherit from?.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • You have me confused with this: "They can do all the things that the class pattern did in ES5". What class pattern in ES5? – jfriend00 Dec 30 '17 at 19:12
  • @jfriend00 The thing that people used to call a "class", i.e. a combination of a constructor function with methods on its prototype object. – Bergi Dec 30 '17 at 19:45
  • He means, you forever did have the possibility to do it has explained here: https://stackoverflow.com/posts/30391861/revisions People really do not understand this langage since after many years worked on it. For more informations just check the "You don't know js" programm Classes -> https://github.com/getify/You-Dont-Know-JS/blob/master/es6%20%26%20beyond/ch3.md#classes – nodeover Dec 30 '17 at 19:45
  • I just think `class pattern in ES5` is a bad choice of words when you are contrasting it to the `class` keyword in ES6. I'd probably suggest being more descriptive such as the `method of manually defining constructor functions and prototypes in ES5`. – jfriend00 Dec 31 '17 at 00:13
  • @jfriend00 I see, but I guess it was kinda deliberate: they are more similar than one might think when looking at the syntax, both methods create basically the same object structure. That structure is what I call a "class", not the code parts that created it. – Bergi Dec 31 '17 at 09:36
2

I'm not sure about es5, but it is possible to simulate es6 classes without using the class syntax, using other es6 features instead.

For example, your example

class MyFunc extends Function{}
new MyFunc('alert("hi guys")')()

Can be simulated using old style classes and Reflect.construct as follows

function MyFunc(...args) {return Reflect.construct(Function, args, new.target);}
new MyFunc('alert("hi guys")')()

Reflect.construct does what the super() call in a real subclass constructor would do, which is all you need for the example posted. If you want to properly inherit all the properties and static methods, you need to additionally set the prototypes like this

function MyFunc(...args) {return Reflect.construct(Function, args, new.target);}
Object.setPrototypeOf(MyFunc, Function);
Object.setPrototypeOf(MyFunc.prototype, Function.prototype);
new MyFunc('alert("hi guys")')()

This also works for the array example

function MyArr(...args) {return Reflect.construct(Array, args, new.target);}
Object.setPrototypeOf(MyArr, Array);
Object.setPrototypeOf(MyArr.prototype, Array.prototype);
var myarr = new MyArr(1,2,3,4)
myarr[2] = "a value"
myarr.push("an other value")

The first set prototype call is only required to inherit static properties like Array.from. If you don't care about those, you can get away with only setting up the prototype of the prototype object, in which case you don't need Object.setPrototypeOf at all. You can instead use Object.create as follows:

function MyArr(...args) {return Reflect.construct(Array, args, new.target);}
MyArr.prototype = Object.create(Array.prototype);
MyArr.prototype.constructor = MyArr;
var myarr = new MyArr(1,2,3,4)
myarr[2] = "a value"
myarr.push("an other value")

For more details, see this post.

Antimony
  • 37,781
  • 10
  • 100
  • 107
  • 1
    In fact you must not omit the newtarget argument to replicate what `super()` does, otherwise `Reflect.construct` is not different any than `new ParentConstructor(...args)`. You probably should also include a `if (!new.target) throw new TypeError(…)`. – Bergi Jan 06 '18 at 18:33
  • @Bergi - good point, I changed the code to make it less confusing – Antimony Jan 06 '18 at 18:37
  • I totally agree. I believe classes are total syntactical sugar given that you have `.setPrototypeOf()` or even the `__proto__` at hand. You can do all kinds of subclassing without classes. – Redu Apr 13 '18 at 20:02
-1

It's sugar because it does in background what you can do with prototype.

You have all the explanations here :

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Inheritance_and_the_prototype_chain

ps: sugar does not mean bad for me, it's clearly easier to read, but some does not like.

For the array you can do :

let arr = new Array(1,2,3,4);
arr[2] = "a value";
arr.push("an other value");

No ?

& I had to say that people do not clearly understand JS. When you 'extend' in es6 you do not create an independent type .. that is the mistake the sugar do in your mind .

Just try this code:

class MyArr extends Array{}
let myarr = new MyArr(1,2,3,4);
myarr[2] = "a value";
myarr.push("an other value");
console.log("ah");

Array.prototype.push = function () {
  console.log('NAH');
}

let a = [];
a.push("an other value");
myarr.push("an other value");

If you change Array prototype methode you will change it in extended Class too by reference.

nodeover
  • 301
  • 1
  • 2
  • 11
  • And, the OP shows an example of extending `Array` which you can't particularly do in ES5 because of the magic `.length` property which ES5 doesn't let you inherit from. – jfriend00 Dec 30 '17 at 18:50
  • Even though I did not downvote you, I just want to say that you don't understand what sugar means in this case, – Trash Can Dec 30 '17 at 18:51
  • What ? I really do not understood what you mean @jfriend00 I edited my exemple to show es5 – nodeover Dec 30 '17 at 19:04
  • Huhhh. There's NO code in your answer at all. I'd be happy for you to show us how to inherit from `Array` in ES5. – jfriend00 Dec 30 '17 at 19:05
  • Just add it , you are too fast ! :p – nodeover Dec 30 '17 at 19:06
  • Now, the code you have in your question just shows using an `Array` object. It doesn't show inheriting from it or deriving a new type of object from an Array. – jfriend00 Dec 30 '17 at 19:06
  • Hum.. I think you do not understand how Javascript works dude, array are just objects with some kind of method polyfill in it. There is no 'type extends' in js. What the extends do is what you have always been able to. Some people say they do not like class because it's sugar and other people do not really understand base JS. When you extends Array type it does not create a new independent 'Classes' like in other langage. If you change the method in the extended class it CHANGE them in the parent 'class'. Check my eg in the edited post – nodeover Dec 30 '17 at 19:26
  • 1
    @nodeover You really cannot accuse jfriend of not understanding JS. He's the #5 top answerer on the topic here on SO :-) – Bergi Dec 30 '17 at 19:49