0 votes
by (720 points)

I have been trying to figure out how to use JS classes in Sugarcube. Here's a really simplified example of what I'd like to do:

In the story Javascript:

class Being {
    constructor(name) {
        this.name = name;
    }
}

Then, in a passage:

<<set $gob = new Being("Goblin") >>

This gets the following error:

<<set>>: bad evaluation: Being is not defined

 

I don't know how to make the class useable within a passage, tried several things, including putting the class declaration in a <script> tag within a passage. No dice. Any suggestions? 

 

1 Answer

+3 votes
by (63.1k points)
selected by
 
Best answer

You need to attach your class to something (probably window):

class Being {
    constructor (name) {
        this.name = name;
    }
}
window.Being = Being; // attach to the global scope
<<set $gob to new Being('goblin')>>
<<print $gob.name>> /% 'goblin' %/

In SugarCube (and probably also Harlowe) JavaScript is executed in its own local scope and in strict mode, and so globals need to be created explicitly.

If you want to use instance methods, you'll need to make those serialize-able by providing a custom toJSON() method that uses the JSON.reviveWrapper() method, and by providing a custom cloning method that returns a new, identical instance of your custom object.

Just in case you aren't aware: the class keyword is just an abstraction, and they don't have as wide support as you might want.  The syntax for creating 'classes' without the class keyword is pretty simple.  It would look something like this in your case:

function Being (name) {
    this.name = name;
}
window.Being = Being;

Or:

window.Being = function (name) {
    this.name = name;
}

And you'd probably want to add methods to the prototype (you need to have a clone() and toJSON() method if you want your custom objects to have instance methods):

Being.prototype = {
    capName : function () {
        return this.name.toUpperFirst();
    },
    clone : function () {
        return new Being(this.name);
    },
    toJSON : function () {
        return JSON.reviveWrapper( 'new Being(' + JSON.stringify(this.name) + ')' );
};

It's generally easier to not use custom classes unless you really gain some benefit from doing so, and favor factory functions and other ways of generating standard JavaScript objects instead.

Note on <script> elements:

HTML <script> elements are executed outside of SugarCube's scope, which is almost never what you want.  Use the <<script>> macro provided by SugarCube, or the <<run>> macro.

by (720 points)
Thanks. Considering I'm not sure what much of what you've said means, I think I'm well over my head with this so I'll just not use custom classes for now as you've suggested. Tks.
...