It looks like you're new here. If you want to get involved, click one of these buttons!
:: Db [script] window.ns = {};//namespace window.ns.Db = class NpcDb { constructor() { this.value = 100; } giveValue() { return this.value; } }
:: StoryInit <<set $db to new ns.Db()>>
:: Sample [nobr] Test. <<print $db.giveValue()>>
Test. 100Via Chrome Developper Console
Object.getOwnPropertyNames(SugarCube.State.active.variables.db.__proto__); ["constructor", "giveValue"]
Test. Error: <<print>>: bad evaluation: State.variables.db.giveValue is not a function
Object.getOwnPropertyNames(SugarCube.State.active.variables.db.__proto__); ["hasOwnProperty", "constructor", "toString", "toLocaleString", "valueOf", "isPrototypeOf", "propertyIsEnumerable", "__defineGetter__", "__lookupGetter__", "__defineSetter__", "__lookupSetter__", "__proto__"]
Comments
I don't have enough energy at the moment to explain the why of it.
And I see that toJSON returns a new ns.Db, so the functions should always reflect what is in the code, correct? I.e I edit my JS to add/change/remove a function.
I'll fiddle with this a bit, it would be very helpful to be able to call methods from the object directly, instead of using a "DbManipulator" class like I implemented
1. First passage: 2. Second passage:
By the way, do you know the mechanics behind:
Also I can't find any info on Google about creating a copy-constructor in Javascript (aka constructor(copy) { this = copy.clone(); }), whats the rundown to avoid using an internal object?
Normally I would suggest looking at SugarCube 2's documention and in paticular the Story Functions or the Native Object Methods sections for information on the story format's Javascript related methods but the clone() function is undocumented so to findout information about that method you will need to look at the relevant source code.
...looks like the samething happens to toJSON, tries to JSON itself or something.
I'll just implement it with an internal container for now.
Though I'dd love to know whats going on with "(revive:eval),(class Db { ..." causing an infinite loop of toJSON. Even if you where to eval the class declaration it shouldn't call the contained functions.
I have no idea what you're asking here.
The code string in my example toJSON() method uses new ns.Db() since that's how you create a new instance of the class—the code string will be evaluated later to revive the instance it was called upon, which will happen in a different scope, so you have to use the globally accessible identifier, ns.Db.
You may add whatever methods to your class you chose. Neither the clone() or toJSON() methods need to know about the rest of your class' behavior, since they create new instances of the class with copies of the existing instance's data—well, toJSON() doesn't, but that's what the code string it returns enables.
I'm unsure if I answered your question.
You can. Why do you think you can't?
The clone() and toJSON() methods need to create a new copy of the instance they're called upon—indirectly in the case of toJSON(), as explained previously. To do so they need to be able to call the class' constructor to instantiate a new object and then to copy the original instance's data over to the new instance, thus finally yielding a full copy of the original instance.
You can pass the class' own data properties in a number of ways. My original example showed how to use a simple object, just a data property bag, to hold the class' data properties so that writing the clone() and toJSON() methods was very simple.
As I noted, however, you do not have to do it that way. For example, using loose properties: Pro: Since it uses doesn't hold its data in a simple object internally, you may access the data properties directly.
Con: As you can see, it make things a bit more complex. Either clone() and toJSON() need to be more complex or, as I did here, there needs to be additional helper methods to allow them to remain simple.
To contrast, I've updated my original example to be similar in the data it holds: Pro: The definitions of clone() and toJSON() are much more simple.
Con: To access the data properties outside of method calls and/or getters/setters, you have to reference (instance)._db.(property), e.g. $db._db.foo.
Both styles work. It's just a matter of where you want the complexity to be.
You don't pass it this. Never pass to the clone() function a reference to an object which has a clone() method if that method itself calls the clone() function, you'll start an infinite recursion chain and overflow the call stack.
That is why my original example passed a simple data-only object to the clone() function, rather than a reference to the class instance.
For example, the wrong way: And the right way:
My alternative example within this post shows another way to do it so that you don't have to use an internal data object for the class itself, but you'll still have to use a simple data object for the clone() and toJSON() methods.
It likely isn't. Without looking at the code—it's been a while—what's likely happening is that since the incoming object is cloned as a matter of course, your broken clone() method is causing a stack overflow at that point.
Concerning the _getData(), instead of manually listing each properly, isn't it possible to do:
To the same effet? I could understand if I wanted only certain properties, but I can't see the downside of using forEach if I want all the properties.
Yes, you could—which would definitely ease the maintenance burden. Though, I don't know if I'd call clone() there, since _setData() calls it.
I would also be clear by calling the data object something like data, rather than copy, to avoid potential confusion.
For example (which I'll do completely as ES6):
PS – Caution: To other potential authors reading this and thinking "I'm going to do that too". As of this writing, I do not yet recommend using ES2015/ES6 in your stories due to legacy browser issues.