I'm using ES6 classes inside my game. I have two questions:
Question 1) I'm instantiating two singleton classes for BattleMode
and ExplorationMode
and passing in an initial parameter type
.
Even though I'm console.logging explorationMode
(an instance of ExplorationMode
), it shows Class BattleMode
with type exploration
. Why is this?
Question 2) I'm using gulp with babel
to compile scripts into main.js
. Both ExplorationMode
and BattleMode
above inherit from parent class Mode
. Gulp runs through modes/
folder and adds them sequentially to main.js
.
Apparently ES6 cares about the order of JavaScript, meaning if BattleMode
is a child of Mode
, then it will say it cannot find its parent.
Uncaught TypeError: Super expression must either be null or a function, not undefined
This forces renaming of mode.js
such that it appears first in the folder order. That's pretty hacky. If I prepend A_mode
, the error goes away.
Is there a better solution for this besides renaming or specifying each parent in gulp to appear before its children?
Otherwise, I could just use Objects:
var Mode = Mode || {};
Mode.BattleMode = Mode.BattleMode || {
doThing : function () {
...
Question 1:
class StateManager {
constructor(type) {
if (!stateManagerInstance) {
stateManagerInstance = this;
}
this.type = type;
const battleMode = new BattleMode('battle');
const explorationMode = new ExplorationMode('exploration');
console.log('MODE: ', explorationMode); // Wrong
...
Console Output:
MODE: Class BattleMode { type: "exploration" , screenState: "battle" }
Mode:
/*
Define as Singleton
*/
let modeInstance = null;
class Mode {
constructor() {
if (!modeInstance) {
modeInstance = this;
}
return modeInstance;
}
}
ExplorationMode:
/*
Define as Singleton
*/
let explorationInstance = null;
class ExplorationMode extends Mode {
constructor(type) {
super(type);
this.type = type;
if (!explorationInstance) {
explorationInstance = this;
}
this.screenState = '';
return explorationInstance;
}
BattleMode:
/*
Define as Singleton
*/
let battleInstance = null;
class BattleMode extends Mode {
constructor(type) {
super(type);
this.type = type;
if (!battleInstance) {
battleInstance = this;
}
this.screenState = '';
return battleInstance;
}
Question 2:
Gulp:
gulp.task('build-dev', function() {
gulp.run('scripts');
gulp.watch('js/**/*.js', function() {
gulp.run('scripts');
});
});
gulp.task('scripts', function() {
return gulp.src([
'js/**/*.js',
])
.pipe(babel({
presets: ['es2015']
}))
.pipe(concatJs('main.js'))
.pipe(gulp.dest('build/js'));
});
This is an example why singleton can be antipattern. It provides extra complexity without real benefits. Children classes don't even benefit from inheritance.
Since both BattleMode
and ExplorationMode
inherit from Mode
, Mode
constructor is evaluated first. The first instance is saved to modeInstance
. Since BattleMode
is instantiated first, it's an instance of BattleMode
, no matter what happens in child constructors.
In this case and many others singleton classes should just be replaced with plain objects.