How to Extends / Alias / Overwrite existing class in MZ/ ES6 [Tutorial]

MushroomCake28

KAMO Studio
Global Mod
Joined
Nov 18, 2015
Messages
3,727
Reaction score
4,679
First Language
English
Primarily Uses
RMMZ
That's not the same thing as redoing the entire source code to make it full ECS. For that to work, every element on the screen would have to be an object to which we attach component, like in Unity. I'm not saying we shouldn't borrow from that system when it serves us, I'm just saying that converting the entire engine just to achieve that is a huge project, not even counting the fact that the editor isn't adapted for that.

As for ES5 vs ES6, I'll be using whatever MZ code is using for all my shared plugins. For my personal plugins for my projects that I won't be sharing I'll be using ES6 though.
 

Galenmereth

Retired
Veteran
Joined
May 15, 2013
Messages
2,248
Reaction score
2,158
First Language
English
Primarily Uses
N/A
The ECS comment was just me talking about game engine design in general. Not saying it would be a good fit for RM’s general userbase, although it could’ve; Unity is very popular and easy to use and extend because of it, from a programmer/plugin maker perspective. To write plugins you already have to be quite an advanced user.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
This is my latest progress on trying to use ES6 class inheritance with aliasing but without directly typing prototypes for plugin developers:
JavaScript:
/*
* Do these 2 additional things when using ES6 class inheritance aliasing
* without directly typing prototypes:
* 1. Add the following code right below a new class inheriting another one:
*    - ExtendedClassAlias.inherit(Klass);
*    Where Klass is the new class inheriting another one
* 2. Add the following code right below extending an existing class as a way
*    to alias its methods:
*    - ExtendedClassAlias.updateClass(Klass);
*    Where Klass is the existing class being extended as a way to alias its
*    methods
* Right now it doesn't work well with inheriting static functions in classes,
* so those in children classes should use ParentClass.staticFunc.call(this)
* instead of super.staticFunc()
*/

// Codes allowing ES6 class inheritance aliasing without direct prototyping
class ExtendedClassAlias {

    static inherit(Child) {
        const childProto = Child.prototype;
        const parentName = Object.getPrototypeOf(childProto).constructor.name;
        this._inherit(Child, parentName);
    }

    static updateClass(Parent) {
        const parentName = Parent.prototype.constructor.name;
        // There's no need to update anything if the passed class's no children
        if (!this._inheritances.has(parentName)) return;
        this._updateClass(this._inheritances.get(parentName), Parent);
        //
    }

    static _inherit(Child, parentName) {
        // So the parent class will know which classes are its children
        if (this._inheritances.has(parentName)) {
            const oldChildProtos = this._inheritances.get(parentName);
            const newChildProtos = oldChildProtos.concat([Child]);
            this._inheritances.set(parentName, newChildProtos);
        } else {
            this._inheritances.set(parentName, [Child]);
        }
        //
    }

    static _updateClass(children, Parent) {
        this._updateProtoMethods(children, Parent.prototype);
        this._updateStaticFuncs(children, Parent);
    }

    static _updateProtoMethods(children, parentProto) {
        // So all the children will inherit the new rather than the old parent
        children.forEach(Child => Child.prototype.__proto__ = parentProto);
        //
    }

    static _updateStaticFuncs(children, Parent) {
        // So all children will inherit all new static functions from new parent
        Object.getOwnPropertyNames(Parent).forEach(name => {
            const desc = Object.getOwnPropertyDescriptor(Parent, name);
            if (!desc || typeof desc.value !== "function") return;
            children.forEach(Child => {
                Child[name] = Child[name] || Parent[name];
            });
        });
        //
    }

}
ExtendedClassAlias._inheritances = new Map();
//
I've tested that it works for adding new instance variables and prototype methods in base classes, and extending and overriding existing prototype methods there(you can place the linked snippet into a JavaScript sandbox and verify the console output yourselves).
While I failed to inherit the changes in the static functions of the base classes from plugins as well, this can be easily mitigated by using BaseClass.staticFunc.call(this) instead of super.staticFunc().

Basically, the essence of the issue when aliasing ES6 class inheritance without direct prototyping is this:
1. The original child class inherits the original base class
2. A plugin extends the original base class to alias some of its prototype methods
3. The child class still inherits the original base class
So to solve this, simply store the linkage between the child class and the base class right after creating that child class, then points the parent of the child class to the extended base class right after extending it.

As for the static functions in classes, while I tried to use the linkage to let the exiting children class use the new static functions from the extended parent class, I failed to cover the case for aliasing existing parent class static functions, because it's just impossible:
1. The super in the static functions of the child class always points to the original parent class
2. The super in the static functions of the extened parent class always points to the original parent class
3. The super in the static functions of the child class's supposed to always point to the extended parent class
Clearly, combining 1 and 2 will contradict with 3, which is the goal I've trying to achieve.

For those not being familiar with ES5 or avoiding prototypes like a plague, I hope using ExtendedClassAlias won't be too demanding for you, as all you need to do is sticking to these:
JavaScript:
/*
* Do these 2 additional things when using ES6 class inheritance aliasing
* without directly typing prototypes:
* 1. Add the following code right below a new class inheriting another one:
*    - ExtendedClassAlias.inherit(Klass);
*    Where Klass is the new class inheriting another one
* 2. Add the following code right below extending an existing class as a way
*    to alias its methods:
*    - ExtendedClassAlias.updateClass(Klass);
*    Where Klass is the existing class being extended as a way to alias its
*    methods
* Right now it doesn't work well with inheriting static functions in classes,
* so those in children classes should use ParentClass.staticFunc.call(this)
* instead of super.staticFunc()
*/
P.S.: I've spent almost 24 hours on this and I enjoyed the process a lot, even though this might not be practical enough to be used in MZ :)
 
Last edited:

Solar_Flare

Veteran
Veteran
Joined
Jun 6, 2020
Messages
533
Reaction score
235
First Language
English
Primarily Uses
RMMV
I was already planning to just continue to use regular prototype aliasing if I ever get MZ, and reading through this thread hasn't done anything to change my mind. The only thing I saw that I might consider doing in the future is storing the prototype in a variable.

JavaScript:
var GA_proto = Game_Actor.prototype;
var old_setup = GA_proto.setup;
GA_proto.setup = function() {...}
 

Dr.Yami

。◕‿◕。
Developer
Joined
Mar 5, 2012
Messages
1,003
Reaction score
757
First Language
Vietnamese
Primarily Uses
Other
I have tried various methods to do stuffs with class since MV first released, decided to stick with prototype in most case. To be honest, except the class X extends Y being shorter, class doesn't offer any other advantage over prototype.

I even have this kind of alias in my Luna Engine, so just use whatever you feel comfortable with

Screenshot 2020-08-08 at 14.59.40.png
 
Last edited:

OcRam

Servant of the Universe
Veteran
Joined
Aug 2, 2016
Messages
614
Reaction score
740
First Language
Finnish
Primarily Uses
RMMZ
I am not sure if this is correct place to post this... but I am worried about this ES6 hype.

Examples:

Arrow (shorthand) functions
Arrow function will use same scope where it's called and 'old' function will use objects scope. For example Game_Map.prototype.eventsXy will scope 'this' object to current instance of Game_Map. On top of that you can't bind arrow function...

MZ codebase at the moment doesn't use ES6 class structure
So ES6 class aliasing isn't something to worry about to extend RMMZ base functions. Of course it is good to know how to do it - for example if you want to extend some other plugins that uses ES6 classes.

Semicolons are not obsolete in ES6
This is just my personal opinion, but JS code should be able to run in single line.
Good article about JS semicolons today found here
 

Solar_Flare

Veteran
Veteran
Joined
Jun 6, 2020
Messages
533
Reaction score
235
First Language
English
Primarily Uses
RMMV
Arrow (shorthand) functions
Arrow function will use same scope where it's called and 'old' function will use objects scope. For example Game_Map.prototype.eventsXy will scope 'this' object to current instance of Game_Map. On top of that you can't bind arrow function...
I think arrow functions are best used for lambdas as arguments to higher-order functions, as they allow referencing the enclosing 'this' without an explicit bind. I don't think I'd use an arrow function anywhere else.

(Note: by higher-order functions I mean things like reduce, map, filter, etc.)

MZ codebase at the moment doesn't use ES6 class structure
So ES6 class aliasing isn't something to worry about to extend RMMZ base functions. Of course it is good to know how to do it - for example if you want to extend some other plugins that uses ES6 classes.
It's not something to worry about regardless of whether the MZ codebase uses ES6 class structure. ES6 classes are just a syntax sugar.

One possible reason for them not to switch everything to ES6 style classes is the JsonEx serialization functions. In MV, at least, these functions won't be able to find the name of an ES6 class, which I assume will break the serialization. I don't know if that's any different in MZ, does anyone know?

(The reason it won't work is because the serializer calls toString() on the constructor function and tries to extract the class name from that by looking for the "function" keyword... but a class declared in ES6 style won't even have the function keyword in its toString(). Fixing this is easy - look for either the "function" or "class" keyword - but I don't know if they would've done that. Or better still, don't call toString() at all and just do ClassName.name, which might even work for the "Class = function() {...}" style, though it can also fail if someone defines Class.name explicitly...)

Semicolons are not obsolete in ES6
This is just my personal opinion, but JS code should be able to run in single line.
Good article about JS semicolons today found here
The semicolon behaviour in JS is really bad, honestly. I'd actually prefer if they made them compulsory, because the automatic semicolon insertion sometimes produces strange results.
 

Dr.Yami

。◕‿◕。
Developer
Joined
Mar 5, 2012
Messages
1,003
Reaction score
757
First Language
Vietnamese
Primarily Uses
Other
I always omit semicolon in js/ts, it looks really ugly and isn't necessary.

Edit: Most cases where semicolon is needed, the code is smelly. Also modern JS is not only revolve about classes and arrow functions. Tbh I use babel in many projects so I don't remember what added between ES version, but I think there are many features that some dev feel useful while the other doesn't.
 
Last edited:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
One possible reason for them not to switch everything to ES6 style classes is the JsonEx serialization functions. In MV, at least, these functions won't be able to find the name of an ES6 class, which I assume will break the serialization. I don't know if that's any different in MZ, does anyone know?

(The reason it won't work is because the serializer calls toString() on the constructor function and tries to extract the class name from that by looking for the "function" keyword... but a class declared in ES6 style won't even have the function keyword in its toString(). Fixing this is easy - look for either the "function" or "class" keyword - but I don't know if they would've done that. Or better still, don't call toString() at all and just do ClassName.name, which might even work for the "Class = function() {...}" style, though it can also fail if someone defines Class.name explicitly...)
It's different in MZ:
Code:
JsonEx._encode = function(value, depth) {
    // [Note] The handling code for circular references in certain versions of
    //   MV has been removed because it was too complicated and expensive.
    if (depth >= this.maxDepth) {
        throw new Error("Object too deep");
    }
    const type = Object.prototype.toString.call(value);
    if (type === "[object Object]" || type === "[object Array]") {
        const constructorName = value.constructor.name;
        if (constructorName !== "Object" && constructorName !== "Array") {
            value["@"] = constructorName;
        }
        for (const key of Object.keys(value)) {
            value[key] = this._encode(value[key], depth + 1);
        }
    }
    return value;
};

JsonEx._decode = function(value) {
    const type = Object.prototype.toString.call(value);
    if (type === "[object Object]" || type === "[object Array]") {
        if (value["@"]) {
            const constructor = window[value["@"]];
            if (constructor) {
                Object.setPrototypeOf(value, constructor.prototype);
            }
        }
        for (const key of Object.keys(value)) {
            value[key] = this._decode(value[key]);
        }
    }
    return value;
};
MZ doesn't look for the function keyword anymore :)
 

OcRam

Servant of the Universe
Veteran
Joined
Aug 2, 2016
Messages
614
Reaction score
740
First Language
Finnish
Primarily Uses
RMMZ
I always omit semicolon in js/ts, it looks really ugly and isn't necessary.

Edit: Most cases where semicolon is needed, the code is smelly. Also modern JS is not only revolve about classes and arrow functions. Tbh I use babel in many projects so I don't remember what added between ES version, but I think there are many features that some dev feel useful while the other doesn't.
I agree that they look ugly :)

BUT omitting semicolons where they are needed may change intended execution behavior.

For example below code block won't work because most JS interpreters thinks that bracket array is property of 0.
Code:
let i = 0
[0, 1, 2, 3, 4, 5, 6].forEach(item => console.log(item))
But if you add semicolon it will work - since we tell JS interpreter that it's end of declaration.
Code:
let i = 0;
[0, 1, 2, 3, 4, 5, 6].forEach(item => console.log(item))
 

LTN Games

Indie Studio
Veteran
Joined
Jun 25, 2015
Messages
704
Reaction score
631
First Language
English
Primarily Uses
RMMV
I agree that they look ugly :)

BUT omitting semicolons where they are needed may change intended execution behavior.

For example below code block won't work because most JS interpreters thinks that bracket array is property of 0.
Code:
let i = 0
[0, 1, 2, 3, 4, 5, 6].forEach(item => console.log(item))
But if you add semicolon it will work - since we tell JS interpreter that it's end of declaration.
Code:
let i = 0;
[0, 1, 2, 3, 4, 5, 6].forEach(item => console.log(item))
I’ve omitted semicolons for over 4 years and never once ran into an issue or “gotcha”, not many people write code like the examples above and if you do then I guess now you know when to use semicolons lol. In fact you’ll likely run into more issues using semicolons than you will omitting them. Even the devs at NodeJS don’t use semicolons. But whatever works for you is what I say

Edit: Also, to add about aliasing, I see no problem with using prototype to alias. I've been writing my plugins in ES6 for years now and I've come accustomed to seeing any classes I've edited within MV to be in prototype format and any of my new written classes are using class syntax.
I personally would not focus so much on how to alias or trying to find new ways to alias and I would stick to the same ol' method and focus on coding lol, I mean the old method is easy and it makes sense. I have found many ways to avoid aliasing all together, while I still alias some methods, its minimal, you'd be surprised how often you can simply create a new method of your own or find a whole new way of doing something that never required aliasing to begin with.
 
Last edited:

Dr.Yami

。◕‿◕。
Developer
Joined
Mar 5, 2012
Messages
1,003
Reaction score
757
First Language
Vietnamese
Primarily Uses
Other
I agree that they look ugly :)

BUT omitting semicolons where they are needed may change intended execution behavior.

For example below code block won't work because most JS interpreters thinks that bracket array is property of 0.
Code:
let i = 0
[0, 1, 2, 3, 4, 5, 6].forEach(item => console.log(item))
But if you add semicolon it will work - since we tell JS interpreter that it's end of declaration.
Code:
let i = 0;
[0, 1, 2, 3, 4, 5, 6].forEach(item => console.log(item))
Yep, this and also anonymous function (function() {})(), but mostly people won't write this way. For your example, it's kind of magic array. But well, there might be some niche cases, I always have a tool to automatically fix those kind of expression, if stuffs happen, I just have to change the way to write so that semicolon can be omitted cuz like I said, the code where semicolon needed was often smelly.
 

OcRam

Servant of the Universe
Veteran
Joined
Aug 2, 2016
Messages
614
Reaction score
740
First Language
Finnish
Primarily Uses
RMMZ
I’ve omitted semicolons for over 4 years and never once ran into an issue or “gotcha”, not many people write code like the examples above and if you do then I guess now you know when to use semicolons lol. In fact you’ll likely run into more issues using semicolons than you will omitting them. Even the devs at NodeJS don’t use semicolons. But whatever works for you is what I say

Edit: Also, to add about aliasing, I see no problem with using prototype to alias. I've been writing my plugins in ES6 for years now and I've come accustomed to seeing any classes I've edited within MV to be in prototype format and any of my new written classes are using class syntax.
I personally would not focus so much on how to alias or trying to find new ways to alias and I would stick to the same ol' method and focus on coding lol, I mean the old method is easy and it makes sense. I have found many ways to avoid aliasing all together, while I still alias some methods, its minimal, you'd be surprised how often you can simply create a new method of your own or find a whole new way of doing something that never required aliasing to begin with.
Yep, this and also anonymous function (function() {})(), but mostly people won't write this way. For your example, it's kind of magic array. But well, there might be some niche cases, I always have a tool to automatically fix those kind of expression, if stuffs happen, I just have to change the way to write so that semicolon can be omitted cuz like I said, the code where semicolon needed was often smelly.
Why I'm worried about this is that one who is learning JS and thinks that semicolons aren't needed anymore might get confused.

Previous example I gave was just one of the many cases. Not everyone writes code the way semicolons aren't needed or use babel / other tools.

But here's 2 more examples:

Code:
let a = 1
let b = a
(a + b).toString() // Will give an error
Or this is unexpected behavior IMO:
Code:
let a = 1
-a + a === 0 ? console.log(0) : console.log(2)
// Is it 0? NOPE without semicolons it will be 2!
let a = 1;
-a + a === 0 ? console.log(0) : console.log(2) // Now it will be 0
But of course everyone writes the code that "floats their boat" - like I do my self (single line rulez! :rock-right:).
 

LTN Games

Indie Studio
Veteran
Joined
Jun 25, 2015
Messages
704
Reaction score
631
First Language
English
Primarily Uses
RMMV
Why I'm worried about this is that one who is learning JS and thinks that semicolons aren't needed anymore might get confused.
Semicolons aren't needed, except for a few special cases, and those few special cases are easy to remember, possibly easier to remember than simply throwing semicolons everywhere or trying to get a newbie to understand all the places to put semicolons.
I'd rather tell someone just learning about JS to not use semicolons, and the only time it would be an issue is when you start a line with any of these [, (, `, +, *, /, -, ,, ., and most people don't start lines like that anyway.

In both examples both worked fine in the console, mind you I don't write code like that either, so for me to run into that issue is unlikely.
1597005623085.png1597005659343.png

I'm not saying you must not use semicolons! I'm just stating its unlikely anyone will run into an issue with omitting them, and as someone who has written without them for over 3 years and never once had a bug or problem because of it, I can say its pretty safe. Besides, look at everyone who omits semicolons

1597006213698.png
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
5,590
Reaction score
6,517
First Language
Indonesian
Primarily Uses
RMVXA
Previous example I gave was just one of the many cases. Not everyone writes code the way semicolons aren't needed or use babel / other tools.
I think we (or just me) get what you're saying. As someone who regularly switches from a language that does need semicolon and the one that doesn't, it isn't really a big deal. Sometimes, when writing JS, I have a habit of omitting it and putting it to feel safe. Most of the time when I omit it, it usually because I forgot. And when I do put them, I simply just want to explicitly put it as the end of the statement. I don't think semicolons are ugly.

I'd rather tell someone just learning about JS to not use semicolons, and the only time it would be an issue is when you start a line with any of these [, (, `, +, *, /, -, ,, ., and most people don't start lines like that anyway.
Since it isn't needed, telling someone not to use a semicolon is not bad advice. In SQL, I discovered that you can use a semicolon to put an end to a query statement. But most of the time it is not really needed. Only in a specific case, you're required to put a semicolon to prevent query ambiguousness, that's it.

You can do the same thing in JS of course. "If you want to write this kind of code, be sure to put a semicolon here".

EDIT:
Btw, it's really off topic
 

Solar_Flare

Veteran
Veteran
Joined
Jun 6, 2020
Messages
533
Reaction score
235
First Language
English
Primarily Uses
RMMV
I'd rather tell someone just learning about JS to not use semicolons, and the only time it would be an issue is when you start a line with any of these [, (, `, +, *, /, -, ,, ., and most people don't start lines like that anyway.
I would tend to disagree on your final point here. I think starting a line with [, (, or ` would actually be pretty common in some styles of code.

In both examples both worked fine in the console, mind you I don't write code like that either, so for me to run into that issue is unlikely.
View attachment 154678View attachment 154679
Well, of course that would work fine. Two lines in the console are not two lines in the same program / file. They are entirely separate scripts. If you pressed shift+enter in order to execute both lines in a single console call, I'm sure you'd see the issue.

I'm just stating its unlikely anyone will run into an issue with omitting them,
While I don't think it's extremely likely, I think it's more likely than you believe. I recall running into an issue with them myself a couple of times.
 

LTN Games

Indie Studio
Veteran
Joined
Jun 25, 2015
Messages
704
Reaction score
631
First Language
English
Primarily Uses
RMMV
I would tend to disagree on your final point here. I think starting a line with [, (, or ` would actually be pretty common in some styles of code.
and if that is someone's style then just put a semicolon at beginning of the line and omit them everywhere else, its still preference and still possible to omit them and make a great codebase.


While I don't think it's extremely likely, I think it's more likely than you believe. I recall running into an issue with them myself a couple of times.
Most times someone who runs into semicolons problems are already using semicolons everywhere else in the code. Like I said it can be an issue, but its pretty easy to remember where you need a semicolon, like the very beginning of an IIFFE or beginning of any line that starts with the above mentioned, if you remember that you will likely never have a problem.

We could argue all day about semicolons, but my 3+ years experience without them is proof enough for me, so yea, I'm done with this, I also don't want to further derail this topic, sorry everyone. If you want to read further on semicolon, check out this fun article https://blog.izs.me/2010/12/an-open-letter-to-javascript-leaders-regarding

Oh, fun little tidbit, creator of JS Brenden Eich also omits semicolons, butttttt he also made JS :stickytongue:
 

OcRam

Servant of the Universe
Veteran
Joined
Aug 2, 2016
Messages
614
Reaction score
740
First Language
Finnish
Primarily Uses
RMMZ
and if that is someone's style then just put a semicolon at beginning of the line and omit them everywhere else, its still preference and still possible to omit them and make a great codebase.



Most times someone who runs into semicolons problems are already using semicolons everywhere else in the code. Like I said it can be an issue, but its pretty easy to remember where you need a semicolon, like the very beginning of an IIFFE or beginning of any line that starts with the above mentioned, if you remember that you will likely never have a problem.

We could argue all day about semicolons, but my 3+ years experience without them is proof enough for me, so yea, I'm done with this, I also don't want to further derail this topic, sorry everyone. If you want to read further on semicolon, check out this fun article https://blog.izs.me/2010/12/an-open-letter-to-javascript-leaders-regarding

Oh, fun little tidbit, creator of JS Brenden Eich also omits semicolons, butttttt he also made JS :stickytongue:
This is also my final answer and sorry all for derailing... But it's important to change ideas and get some new perspectives.

It was good article about understanding JS and totally agreed on everything he said (especially "If you don’t understand how statements in JavaScript are terminated, then you just don’t know JavaScript very well").
BUT my point was that semicolons are not obsolete.

And you can't run JS in single line if there are no semicolons to end statement - Not even JS leaders can do this (or any referenced institute/organization/individual)...
Code:
let i = 0 let j = 1 ... // Errors
let i = 0; let j = 1; ... // No errors

let obj = function {} obj.prototype.x = function () {} let y = 1 function b() {} let a = '' // Several errors
let obj = function () {}; obj.prototype.x = function () {}; let y = 1; function b() {} let a = ''; // No errors...
// Notice that for example function b() {} doesn't need semicolons even in single line mode
I have been programmed with different languages all my life (now about 30 years and JS about 12 years) and I've seen these pit falls many times during this time... Just trying to help others here.
 

Kino

EIS Game Dev
Veteran
Joined
Nov 27, 2015
Messages
556
Reaction score
794
First Language
English
Primarily Uses
RMMV
JS uses automatic semicolon insertion anyway. As someone who is new to JS they will learn whether to use them or not.
I think it's important to just understand edge cases. Also, as said above if you do want to write in a single line then go for putting semi colons between statements and expressions.
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

I really need to stop adding more levels to this thing already...
My computer is now on the dead list. Parts and Services stopped two days ago for this type.
someone help my thread has been silent since the initial post I just wanna make a goddamn game
People3_6 and People3_7 added!

Meteo Chronicles is almost finished!

Forum statistics

Threads
105,832
Messages
1,016,807
Members
137,533
Latest member
Fluffychu
Top