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

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
Hi there!

As MZ is now officially confirmed to be full ES6 us programmer can now use the ES6 javascript as the official javascript coding standard.

ES6? what is the differences between ES5?
Well here's a quick look at how class are defined in ES5 and ES6
Code:
// ES5 class

function Scene_Dummy() { this.initialize.apply(this, arguments); }
Scene_Dummy.prototype = Object.create(Scene_Base.prototype);
Scene_Dummy.prototype.constructor = Scene_Dummy;

Scene_Dummy.prototype.initialize = function(){
    Scene_Base.prototype.initialize.call(this);
};

Scene_Dummy.prototype.createFadeSprite = function(white){
    Scene_Base.prototype.createFadeSprite.call(this, white);
};

// ES6 class

class Scene_Dummy extends Scene_Base {

    constructor(){
        super();
        this.initialize.apply(this,arguments); // should not be existing anymore for MZ but just in case
    }

    initialize(){
        super.initialize();
    }

    createFadeSprite(white){
        super.createFadeSprite(white);
    }
}
As you can see, it's make the code WAY more clean and shortened!
Although now you might ask : Do I use the same syntax when I Overwrite, Alias or extends an existing class?
The short answer is : Yes and no.

the longer answer is No since Class in ES6 are considerate final and can't be redeclared
so if you did
Code:
class Something {

}

class Something {

}
It would throw an error of duplicate member.
So how do you actually extends existing class then???

The short answer : The good old MV way!

The long answer : ES6 is just syntactic sugar and in concept is still ES5 prototype class and still behave like ES5 just more 'stricter' and prettified.

so here's the method to overwrite/ alias and extends an existing class!
Code:
// alias
const alias = Scene_Base.prototype.createFadeSprite;
Scene_Base.prototype.createFadeSprite = (white) => { // Just a shortend for 'function'
alias.call(this, white);
// new content
};

// Overwrite
Scene_Base.prototype.createFadeSprite = (white) => { // Just a shortend for 'function'
// new overwritten content
};

// Extends existing class.

Scene_Base.prototype.newFunction = () => {
    // content
};

From @Galenmereth
He showed me how to extends existing class using ES6 syntax thanks to him!
Code:
// named function.
class Scene_Alias extends Scene_Base {
    create(){
    super.create();
    this.something();
    }
    
    something(){
    
    }
}

Scene_Base = Scene_Alias; // Will do the same thing than the es5 counterpart without having a global!

// anonymous class for avoid nameclashing.
Scene_Base = class extends Scene_Base {
  constructor(){
  super();
  }
 
  create(){
  super.create();
  this.something();
  }
 
  something(){
 
  }
}

EXTRA NOTE :

Every function from ES5 will still work so you can still declare class like how it worked in MV!

On That I hope it helped you and will remove any worries you had for MZ plugin development!
Happy Game dev!


On a quick note : I know it's very basic ES6 programming but for people using MZ they might google : how to extends function in MZ.
 
Last edited:

Solar_Flare

Veteran
Veteran
Joined
Jun 6, 2020
Messages
523
Reaction score
230
First Language
English
Primarily Uses
RMMV
so you can still declare class like how it worked in MV!
Is this confirmed? I'm asking because the implementation of JsonEx._getConstructorName appears to depend on declaring the class in a certain way, and it would be good to know if the ES6 way works with that.

If ES6 classes work with JsonEx in MV, that should be good enough as confirmation.

(Personally, I don't care for ES6 classes. I suppose they're sort of a useful syntactic sugar, but if I recall correctly, they paper over the range of possibilities you can achieve with the old method.)
 

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
I dont know if they would work in mv since the tutorial was catter for mz
 

MushroomCake28

KAMO Studio
Global Mod
Joined
Nov 18, 2015
Messages
3,523
Reaction score
4,534
First Language
English
Primarily Uses
RMMV
This is a very useful thread.

I've stickied this thread for now.

 

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
From @Galenmereth
He showed me how to extends existing class using ES6 syntax thanks to him!
Code:
// named function.
class Scene_Alias extends Scene_Base {
    create(){
    super.create();
    this.something();
    }
    
    something(){
    
    }
}

Scene_Base = Scene_Alias; // Will do the same thing than the es5 counterpart without having a global!

// anonymous class for avoid nameclashing.
Scene_Base = class extends Scene_Base {
  constructor(){
  super();
  }
 
  create(){
  super.create();
  this.something();
  }
 
  something(){
 
  }
}
thanks again Galen!
 

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
So basically nothing's changed? lol
nope! for extending class you can either go ES5 prototype or you can do the Galenmereth technique.
but many people who begin javascript will try to redeclare the class lol.
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,553
Reaction score
3,789
First Language
English
nope! for extending class you can either go ES5 prototype or you can do the Galenmereth technique.
but many people who begin javascript will try to redeclare the class lol.
How do I alias a method with the new syntax?

Code:
class MyClass {
   something() {
      return 1;
   }
}

// new plugin
MyClass = class extends MyClass {
  // ???? 
  something() {
     // return previous value + 2?
 }
}
 

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
with the (super) call since it's work the same as '.call' from the ES5

in simple super.method(arguments) = Alias.call(this,arguments)

@Tsukihime
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,553
Reaction score
3,789
First Language
English
with the (super) call since it's work the same as '.call' from the ES5

in simple super.method(arguments) = Alias.call(this,arguments)

@Tsukihime
I tried it and it works lol
But kind of strange that it works. It's a strange place to use "super" since by design there isn't a MyClass parent. I'd probably get really confused if I see the super there.

Anyways so I tried something else

Code:
class MyClass {
   something() {
      return 1;
   }
}

class Sub extends MyClass {
  something() {
    return super.something();
  }
}

a = new Sub();
console.log(a.something()) // prints 1

MyClass = class extends MyClass {
  something() {
   return super.something() + 2;
}
}

console.log(a.something()) // prints 1
a = new Sub();
console.log(a.something()) // still prints 1???
The first print will say "1" because sub inherits from MyClass. Which is expected.

But then when I change MyClass, sub doesn't inherit the change.

And when I create a new Sub, thinking maybe it's using the old methods, it STILL prints 1???
This is going to be problematic I think.
 
Last edited:

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
I tried it and it works lol
But kind of strange that it works. It's a strange place to use "super" since by design there isn't a MyClass parent. I'd probably get really confused if I see the super there.

Anyways so I tried something else

Code:
class MyClass {
   something() {
      return 1;
   }
}

class Sub extends MyClass {
  something() {
    return super.something();
  }
}

a = new Sub();
console.log(a.something()) // prints 1

MyClass = class extends MyClass {
  something() {
   return super.something() + 2;
}
}

console.log(a.something()) // prints 1
a = new Sub();
console.log(a.something()) // still prints 1???
The first print will say "1" because sub inherits from MyClass. Which is expected.

But then when I change MyClass, sub doesn't inherit the change.

And when I create a new Sub, thinking maybe it's using the old methods, it STILL prints 1???
This is going to be problematic I think.
I see I will ping @Galenmereth about this.
 

Ossra

Formerly Exhydra
Veteran
Joined
Aug 21, 2013
Messages
1,076
Reaction score
845
First Language
English
Primarily Uses
RMMV
People over on the Japanese forum are attempting to work out the issues, as well (you are mentioned @nio kasgami). Someone ran into the same issue. One of the offered solutions used the following :

Code:
class MyClass {
   something() {
      return 1;
   }
}

class Sub extends MyClass {
  something() {
    return super.something();
  }
}

var a = new Sub();

console.log(a.something()); // 1

const Old_Something = MyClass.prototype.something;

MyClass.prototype.something = function () {
  return Old_Something.call(this) + 2;
}

console.log(a.something()); // 3

var a = new Sub();

console.log(a.something()); // 3


Or you could use ...



Code:
class MyClass {
   something() {
      return 1;
   }
}

class Sub extends MyClass {
  something() {
    return super.something();
  }
}

a = new Sub();

console.log(a.something()); // 1

const Old_Something = MyClass.prototype.something;

const somethingMixin = {
  something () {
    return Old_Something.call(this) + 2;
  }
}

Object.assign(MyClass.prototype, somethingMixin);

console.log(a.something()); // 3

a = new Sub();

console.log(a.something()); // 3

Eeh ... none of the solutions looks 'sexy'. I kind of wish someone on the developer team would come forward. Surely someone has created a few default plugins.
 

nio kasgami

VampCat
Veteran
Joined
May 21, 2013
Messages
8,945
Reaction score
3,029
First Language
French
Primarily Uses
RMMV
yeah this is why I am asking galenmereth about it.
 

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,553
Reaction score
3,789
First Language
English
I went back to ruby to see how it looks

Code:
# Base Case
class MyClass
  def number
    return 1
  end
end

class Sub < MyClass
  def number
    return super * 2
  end
end

a = Sub.new
p a.number # 2, because 1 * 2

# Common aliasing that we do
class MyClass

  alias :old_number :number
  def number
    return old_number + 100
  end
end

p a.number # 202, now (1 + 100) * 2
So good. So clean. This is the ideal syntax but of course javascript is going to throw wrenches at us.

I guess the issue is related to extend and how it affects the original class' prototype.
When we say something like

Code:
MyClass = class extend MyClass
We create a new MyClass prototype, but the sub class is still pointing to the OLD MyClass prototype.
 
Last edited:

Galenmereth

Retired
Veteran
Joined
May 15, 2013
Messages
2,248
Reaction score
2,141
First Language
English
Primarily Uses
N/A
So the problem is that in these examples, class `Sub` has already extended class `MyClass`. Even if you change the definition of `MyClass` after the fact, `Sub` doesn't change because it's still extended the original `MyClass` from where it was parsed. The file is parsed top to bottom; at the time when `Sub` was extending `MyClass` is the snapshot it will retain, regardless of whether `MyClass` is then changed later. The only solution to that is to use prototype overrides and ignore inheritance, which isn't what we do today.

If you want to keep changing and extending `MyClass` in the same manner that we do with prototype today in MV's style, it looks like this with the class syntax:

Code:
class A {
    print() {
      return "A";
  }
}

let a = new A();

console.log("Before extend", a.print()); // Prints "A"

A = class extends A {
    print() {
      return super.print() + "B";
  }
}

a = new A();

console.log("First extend", a.print()); // Prints "AB"

A = class extends A {
    print() {
      return "C" + super.print();
  }
}

a = new A();

console.log("Latest extend that prepends to print()", a.print()); // Prints "CAB
Imagine in each of the two extending cases, it's a different plugin doing it. Just like today, if a plugin later extends the prototype method of an object, if any plugin has done so before that, you'd be handed that plugin's extended prototype method as your "super" when calling it.

If you wanted to make a separate class that extends class `A` in your plugin, that would still work alongside overrides from other plugins.

@Tsukihime The example above is practically doing exactly what's happening in the ruby code, except you don't have to type out the alias; `super.number()` does that for you :)
 
Last edited:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,553
Reaction score
3,789
First Language
English
So the problem is that in these examples, class `Sub` has already extended class `MyClass`. Even if you change the definition of `MyClass` after the fact, `Sub` doesn't change because it's still extended the original `MyClass` from where it was parsed. The file is parsed top to bottom; at the time when `Sub` was extending `MyClass` is the snapshot it will retain, regardless of whether `MyClass` is then changed later. The only solution to that is to use prototype overrides and ignore inheritance, which isn't what we do today.
Ya that's mostly the issue.
In the sub example, I would want changes to the parent's methods to be inherited as well.

Code:
Parent: return 1
Child: Return parent's value * 2, // so we get 2
Parent: return 2
Child: Still thinks parent is returning 1. // we still get 2, instead of 4
So basically the extend syntax won't help here, and we'd have to go back to prototype aliasing.

Code:
var old_method = method
Parent.prototype.method = function() {
   return 2;
}
Which isn't really too bad I mean I kind of liked not having to type "prototype" so much lol but if there's no option then we'd have to just settle with the old way.
 

Galenmereth

Retired
Veteran
Joined
May 15, 2013
Messages
2,248
Reaction score
2,141
First Language
English
Primarily Uses
N/A
Code:
// I added missing "Parent.prototype.method" syntax here
var old_method = Parent.prototype.method;
Parent.prototype.method = function() {
   return 2;
}

// The following is effectively the same as the above in terms of how the JS engine eventually compiles the code

Parent = class extends Parent {
    method() {
        return 2;
    }
}
The only difference is you saved a ref to the original prototype method, however any other plugin would never see that and have access to it anyway. You could've done the same in the class extends example, though, as the class extend syntax is still prototype inheritance; they're interchangeable, as we've seen, but the latter is cleaner.
 
Last edited:

Tsukihime

Veteran
Veteran
Joined
Jun 30, 2012
Messages
8,553
Reaction score
3,789
First Language
English
@Galenmereth

Hmm. What about this:

Code:
// our base soldier class
class Pawn {
  get hp() {
    return 100;
  }
}

// some special unit with twice the HP
class Tank extends Pawn {
  get hp() {
    return super.hp * 2
  }
}

console.log(new Pawn().hp) // 100
console.log(new Tank().hp) // 200. As expected

// NEW PLUGIN that gives all of our units 1000 HP bonus!
Pawn = class extends Pawn {
  get hp() {
    return super.hp + 1000
  }
}

console.log(new Pawn().hp) // 1100. Works well.
console.log(new Tank().hp) // still 200. Doesn't apply new logic
Is there a way to get the tank's HP to reflect the huge boost that the pawn got?
 

TheoAllen

Self-proclaimed jack of all trades
Veteran
Joined
Mar 16, 2012
Messages
5,573
Reaction score
6,496
First Language
Indonesian
Primarily Uses
RMVXA
I'm following this, but @Galenmereth will you mind removing the javascript syntax color in your code tag? Because it looks horrible in the dark theme and I don't think this will get fixed.
Screenshot_23.jpg

EDIT:
@Tsukihime the only thing I could think of it just the load order issue.
The new plugin to give the pawn HP boost must be placed above the tank plugin.

I'm still thinking that aliasing the prototype function and replacing it still more robust than ES6 syntax somehow.
 
Last edited:

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

Latest Threads

Latest Posts

Latest Profile Posts

I've had some hard times. A co-worker accused me (not going into detail here), and I am trying finish training next week. Still not paid yet, I think next week.
I don't like hard boiled eggs...
it's weird to eat something that crunchy outside soft inside. And after chewing it for a while it started to spill red sauces that taste like iron.
Latest member to OcRam -plugin suite: OcRam_Layers! Optimized parallax layers (for parallax mapping?) + dynamic layers which can be used in title-, battle and map scenes!
People convinced me to use my old graphics for this new project, i guess i can't run away from blender just yet. https://gyazo.com/2a105847c042bb78c6d4919ec1308660

Forum statistics

Threads
102,963
Messages
996,323
Members
134,428
Latest member
productscrack
Top