© 2019 Meouzer Consortium

JavaScript: Serialization/Deserialization of E6 Classes

loading
We show how to copy singly scoped class instances. The Russians have worked on this technology for years with little success. After initial concerns about findings making their way to enemy K9 units, publishing went ahead since American feline intelligence has known for years that Russian K9's don't understand JavaScript.
Meouzer

Introduction

Open the console window to see that all tests of Test-Ser-Des.js have been passed.

For ES6, serialization/deserialization is more intrusive than for ES5. A value copy of a class instance x of foo, where foo is an ES5 class is simply y = Object.create(Object.GetProptotypeOf(x)). This doesn't work in ES6 because of private fields. If foo is an ES6 class then a value copy of x can only be obtained through a call to a "default" constructor of foo, which is to do absolutely nothing, but in doing nothing the private fields are automatically initialized for the value copy. Of course default constructors don't exist so we have to emulate such. If the constructor receives defaultConstructorSymbol as its first parameter, the constructor immediately returns having done nothing but produce the value copy. Otherwise the constructor routes code to an #initialize() method which is the "true" constructor. This is all simple as is writing the serialization and deserialization routines as seen below.

The How to Do It Section

The Serialization and Deserialization Functions of the foo Class
const defaultConstructorSymbol = Symbol("defaultConstructorSymbol"); // global class foo { #F; // defined in #initialize #G = ...; // depends on variables outside the class definition #H = ...; // doesn't depend on variables in constructor/initializer, and   // doesn't depend on variables outside the class definition. #initialize(x,y) { var a = ...; const b = ...; let c = ...; this.#F = ...; (function nestedFunction(x,y) { // no class methods defined/modified inside any // nestedFunction. changes to variables of the class allowed })(5,7); } constructor(x,y) { if(arguments[0] === defaultConstructorSymbol) return; this.#initialize(x,y); // Don't write constructor code here instead of using #initiliaze. // Variable declarations and function definitions are hoisted, // which might cause a memory leak, when the default constructor // is used. } this.serialize = function() { var cleanContextObject = {this$:this, a:a, b:b, c:c, x:x, y:y, "F":this.#F, "#G":this.#G}; // Include "#H":this.#H if you want. return serializeClassData(cleanContextObject); } static deserialize = function(str) { return (function () { this.evaluator = function(){return eval('('+ arguments[0] +')');}; this.contextObject = parseDataString(str, this.evaluator, new foo(defaultConstructorSymbol), "foo"); var a = this.contextObject.a; const b = this.contextObject.b; let c = this.contextObject.c; var x = this.contextObject.x; var y = this.contextObject.y; this.contextObject.this$.#F = this.contextObject['#F']; this.contextObject.this$.#G = this.contextObject['#G']; return this.contextObject.this$ }).call({}); } }