在Node.js中反序列化之后,将对象与其类重新关联
我正在为一些特定于应用程序的对象编写一个简单的序列化/反序列化框架。
考虑以下:
"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
在这一点上,人们可以问“ d1
有什么d2
缺乏?”
部分工作的一种方法是手动将d1的方法分配给d2:
> d2.constructor = d1.constructor > d2.getName = d1.getName > d2.getName() 'fido'
这有几个缺点。 首先,我必须手动将d1的每个方法分配给d2。 其次,d2获取自己的属性,并不使用原型机制共享插槽:
> d2 Dog { _name: 'fido', constructor: [Function: Dog], getName: [Function] }
所以我提炼的问题是:给定一个对象(例如d2
),是否有办法将它与另一个对象的原型(例如d1
)相关联,以便它inheritance相同的行为?
Object.create()
和Object.getOwnPropertyDescriptors()
是你需要的。
const obj = JSON.parse(JSON.stringify(d1)) const d3 = Object.create(Dog.prototype, Object.getOwnPropertyDescriptors(obj))
这和OP的方法之间的区别在于,这个方法在原型上设置了prototype
属性,而OP的方法直接在对象上设置属性。 当你使用hasOwnProperty()
方法使用for-in循环遍历对象自己的属性时,你可以看到这一点:
for (const i in d1) { if (d3.hasOwnProperty(i)) { console.log(i) } }
用我的方法,它只输出_name
,但用OP的方法,它也输出getName
。
不幸的是, Object.getOwnPropertyDescriptors()
是ECMAScript 2017的一部分,现在只支持Firefox,所以你需要使用Babel。
或者,您可以使用Object.setPrototypeOf()
。 它比Object.getOwnPropertyDescriptors()
有更好的浏览器支持,但MDN不鼓励它,因为它很慢。
const d3 = JSON.parse(JSON.stringify(d1)) Object.setPrototypeOf(d3, Dog.prototype)
在写这篇文章的时候,我想到了创build一个使用反序列化的JSON来初始化对象的自定义构造函数:
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'
更新:如何dynamic查找类并分配原型
正如@Louis所指出的,@ Gothdo的答案要求你知道反序列化对象属于哪个类。 如果您愿意将类名添加到序列化对象中,则可以使用它来dynamic确定类。 所以,举个例子,在OP的例子上展开:
> var d1 = new Dog('fido'); > d1['_class'] = 'Dog'; > let jsonString = JSON.stringify(d1) '{"_name":"fido","_class":"Dog"}'
使用反序列化JSON到JAVASCRIPT对象 (但针对Node.js进行了调整)中描述的技巧,可以使用string通过Node.js的global
对象获取类原型的句柄:
> global[d1['_class']].prototype Dog { getName: [Function] }
现在,您可以使用它来使用@ Gothdo的技术来dynamic重build对象。 把它放在一起:
/** * 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' }