© 2019 Meouzer Consortium

JavaScript: Deep Copying User Classes

Meouzer The Curiously Classy Copy Cat Making JavaScript Great Again meouzer@gmail.com
loading
Simon, our infiltration specialist, was able to install a hidden camera in the JavaScript committee's conference room. Here he is watching the committe who obviously is up to something scary.
Meouzer

Full code is at deepCopy.js, which depends on valueCopy.js, which depends on typing.js. The test file is Test-Class-Copy.js, which uses deepCopySystem to deep copy a user class instance. Search for this.deepCopy in the test file to see how it goes. Open your browser's console window to see that all current tests have been passed. If you are using IE11, then you must comment out all code attempting to change constants because such is a fatal error at compile time. To see how this.deepCopy() works see this.deepCopyExpanded() in the test file, or read below.

Introduction

The term user is used as an abbreviation for programmer, not a catnip fanatic, though most of the members of the consortium are in fact catnip lovers to the extreme.

We may speak colloquially and say that a user class can be deep copied. What is meant is that class instances of the user class can be deep copied with a deepCopy() method.

Given a particular user defined class there's a good chance you can deep copy the class by using deepSystemCopy in a deepCopy() method.

The main requirement that makes a user class deep copiable is that no class methods are defined in a nested function of the constructor. Another rule is that if there are any references to class instances of another user class, then that other class must have its own deepCopy() method to perform deep copies (currently such references are not supported).

The How to Do It Section

The deepCopy() Method of the foo Class
function foo(x,y) { // variables. functions that aren't methods are variables. var a = ...; const b = ...; let c = ...; // Write methods here. // Methods that are getters/setters allowed. (function nestedFunction(x,y) { // no class methods defined inside nestedFunction // changes to variables of the class allowed })(5,7); this.deepCopy = function() { return eval(deepCopyClass)({this$:this, a:a, "const b":b, "let c":c, x:x, y:y}).this$; // This is good! Don't make the mistake of using a local variable, // because it would end up in context of the functions of the // deep copy. } } // That's all there is to it.
A rough expansion of this.deepCopy() so you can see how it works.
However, this expansion introduces unnecessary context.
\numbersRestart this.deepCopy = function() { var contextObject = {this$:this, a:a, "const b":b, "let c":c}; var cleanContextObject = {this$:this, a:a, b:b, c:c}; function foo () { this.evaluator = function(){return eval('('+ arguments[0] +')');}; this.contextObject = deepCopy(cleanContextObject, this.evaluator, true); var a = this.contextObject.a; var b = this.contextObject.b; var c = this.contextObject.c; return this.contextObject; } var dcs = foo.call({contextObject:contextObject}); return dcs.this$; // contextObject, cleanContextObject, foo, and dcs, // all end up in the context of copied methods. Bad! // Yes you can! You can fix that if you want. }

Look at function foo(). Line 6 implies that the deep copy depends upon the evaluator While lines 7-10 say that the evaluator depends upon the deep copy. You might think this is cause for some concern. However, some thinking and a careful reading of the first article on function copying will bring up an important fact about evaluators that makes it clear why everything is A-OK.