Cloning a Javascript object with its type

64 Views Asked by At

There are a lot of questions talking about cloning a JS object, but it seems this one does not exist.

I have a function A extending a function B as follows:

function A () {}
function B () {}
B.prototype = new A()

I then have an object b of type B. I'd like to clone it, and preserve its type, so that the clone of b would be of type B.

Here is how I'm cloning it: (jsfiddle)

function A() {}

function B() {}
B.prototype = new A()

var b = new B()
var bPrime = new b.constructor()
$("#a").text(b instanceof A)
$("#b").text(b instanceof B)
$("#aPrime").text(bPrime instanceof A)
$("#bPrime").text(bPrime instanceof B)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
b is of type A: <span id="a"></span>
<br>b is of type B: <span id="b"></span>
<br>bPrime is of type A: <span id="aPrime"></span>
<br>bPrime is of type B: <span id="bPrime"></span>

In my example, the clone is of type A. How could I have a clone of b that is typed B?

3

There are 3 best solutions below

0
On

You need to set B.prototype.constructor back to B. As it is, it inherits from A, and the constructor is A. So when you do new b.constructor(), you're actually getting a new A().

function A() {}

function B() {}
B.prototype = new A()
B.prototype.constructor = B

var b = new B()
var bPrime = new b.constructor()
$("#a").text(b instanceof A)
$("#b").text(b instanceof B)
$("#aPrime").text(bPrime instanceof A)
$("#bPrime").text(bPrime instanceof B)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
b is of type A: <span id="a"></span>
<br>b is of type B: <span id="b"></span>
<br>bPrime is of type A: <span id="aPrime"></span>
<br>bPrime is of type B: <span id="bPrime"></span>

0
On

Object.create()

The Object.create() method creates a new object with the specified prototype object and properties.

function A() {}

function B() {}
B.prototype = new A()

var b = new B();

var c = Object.create(b);
console.log(c instanceof B);    // -> true

fiddle

0
On

Clone is a pretty ambiguous word. If you want to create a totally new object with no references to anything related to the source object, that's difficult to do without knowing something about the object in question in advance, though you can do wonky prototype-based stuff if you want (not particular advisable). In ES6, it becomes literally impossible to reliably deep clone things in this "property-by-property" sense because of the possibility of an object / constructor / prototype possessing private symbols, WeakMaps, etc.

However you can use Object.create(b) to create a new object that takes b as its own prototype. If you replace

var bPrime = new b.constructor()

with

var bPrime = Object.create(b)

Then you get four trues. However, since b acts as the prototype of bPrime here, changes to b will be reflected in bPrime unless bPrime provides its own definitions that override those things. This is probably not desired for cloning purposes because:

b.prop; // undefined
bPrime.prop; // undefined
b.prop = 7;
bPrime.prop; // 7!

But there’s some refinement we can do:

var bPrime = Object.create(Object.getPrototypeOf(b));

Now you get four trues, but further modifications to b will not be reflected on bPrime.