Reassociando um object com sua class após a desserialização no Node.js

Estou escrevendo uma estrutura simples de serialização / desserialização para alguns objects específicos de aplicativos.

Considere o seguinte:

"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 

Neste ponto, pode-se perguntar “O que tem d1 que d2 não tem?”

Uma abordagem que funciona parcialmente é atribuir manualmente os methods de d1 a d2:

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

Isso tem algumas desvantagens. Primeiro, tenho que atribuir manualmente cada método de d1 a d2. Em segundo lugar, d2 obtém suas próprias propriedades e não compartilha slots usando o mecanismo de protótipo:

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

Então, minha pergunta refinada é: dado um object (por exemplo, d2 ), existe uma maneira de associá-lo ao protótipo de outro object (por exemplo, d1 ) para que ele herde o mesmo comportamento?

Object.create() e Object.getOwnPropertyDescriptors() é o que você precisa.

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

A diferença entre este e o método de OP é que este método define propriedades de prototype no protótipo, enquanto o método de OP define propriedades diretamente no object. Você pode ver isso quando percorrer as propriedades do próprio object usando o loop for-in com o método hasOwnProperty() :

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

Com o meu método, ele gera somente _name , mas com o método de OP ele também gera o getName .

Infelizmente, Object.getOwnPropertyDescriptors() faz parte do ECMAScript 2017 e é suportado apenas no Firefox por enquanto, então você precisará usar o Babel.


Como alternativa, você pode usar Object.setPrototypeOf() . Ele tem melhor suporte ao navegador que Object.getOwnPropertyDescriptors() , mas é desencorajado pelo MDN, porque é lento.

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

Enquanto escrevia isso, tive a ideia de criar um construtor personalizado que usa o JSON desserializado para inicializar o object:

 Dog.createFromJSON = function(obj) { var d = new Dog(); Object.keys(obj).forEach(function(key) { d[key] = obj[key]; }); return d; } > d3 = Dog.createFromJSON(JSON.parse(JSON.serialize(d1))) > d3 Dog { _name: 'fido' } > d3.getName() 'fido' 

Atualização: como encontrar dinamicamente a class e atribuir o protótipo

Como aponta @Louis, a resposta de @Gothdo requer que você saiba a que class o object desserializado pertence. Se você estiver disposto a adicionar o nome da class ao object serializado, poderá usá-lo para determinar a class dinamicamente. Então, por exemplo, para expandir o exemplo do OP:

 > var d1 = new Dog('fido'); > d1['_class'] = 'Dog'; > let jsonString = JSON.stringify(d1) '{"_name":"fido","_class":"Dog"}' 

Usando o truque descrito no object desserializar JSON para JAVASCRIPT (mas ajustado para Node.js) você pode usar uma string para obter um identificador para um protótipo de class através do object global do Node.js:

 > global[d1['_class']].prototype Dog { getName: [Function] } 

Agora você pode usar isso para reconstruir dinamicamente o object usando a técnica do @Gothdo. Juntando tudo:

 /** * Dynamically create an object from a JSON string of properties. * Assumes the presence of a _class meta-property that names the * resulting class. */ function reconstitute(jsonString) { let obj = JSON.parse(jsonString); let cls = global[obj['_class']]; delete obj['_class']; // remove meta-property return Object.setPrototypeOf(obj, cls.prototype); } > reconstitute('{"_name":"fido","_class":"Dog"}') Dog { _name: 'fido' } 
    Intereting Posts