fearless_fool fearless_fool - 3 months ago 7
Javascript Question

Re-associating an object with its class after deserialization in Node.js

I'm writing a simple serialization / deserialization framework for some application-specific objects.

Consider the following:

"use strict";
function Dog(name) { this._name = name; };
Dog.prototype.constructor = Dog;
Dog.prototype.getName = function() { return this._name; }

var d1 = new Dog('fido');
var d2 = JSON.parse(JSON.stringify(d1)); // serialize / deserialize

> d1
Dog { _name: 'fido' }
> d1.getName()
'fido'
> d2
{ _name: 'fido' }
> d2.getName()
TypeError: d2.getName is not a function


At this point, one can ask "What does
d1
have that
d2
lacks?"

One approach that partially works is to manually assign the methods of d1 to d2:

> d2.constructor = d1.constructor
> d2.getName = d1.getName
> d2.getName()
'fido'


This has a couple of disadvantages. First, I have to manually assign each method of d1 to d2. Second, d2 gets its own properties, and doesn't share slots using the prototype mechanism:

> d2
Dog {
_name: 'fido',
constructor: [Function: Dog],
getName: [Function] }


So my refined question is: given an object (e.g.
d2
), is there a way to associate it with the prototype of another object (e.g.
d1
) so it inherits the same behavior?

Answer

Object.create() and Object.getOwnPropertyDescriptors() is what you need.

const obj = JSON.parse(JSON.stringify(d1))
const d3 = Object.create(Dog.prototype, Object.getOwnPropertyDescriptors(obj))

The difference between this and OP's method is that this method sets prototype properties on the prototype, whereas OP's method sets properties directly on the object. You can see this when you loop through object own properties using for-in loop with hasOwnProperty() method:

for (const i in d1) {
  if (d3.hasOwnProperty(i)) {
    console.log(i)
  }
}

With my method it outputs only _name, but with OP's method it outputs also getName.

Unfortunately, Object.getOwnPropertyDescriptors() is part of ECMAScript 2017 and it's supported only in Firefox for now, so you'll need to use Babel.


Alternatively, you can use Object.setPrototypeOf(). It has better browser support than Object.getOwnPropertyDescriptors(), but it's discouraged by MDN, because it's slow.

const d3 = JSON.parse(JSON.stringify(d1))
Object.setPrototypeOf(d3, Dog.prototype)

See also Setting an Object's prototype in Documentation.