【问题标题】:Using JSON.stringify on custom class在自定义类上使用 JSON.stringify
【发布时间】:2012-11-15 09:46:36
【问题描述】:

我正在尝试在redis中存储一个对象,它是一个类的实例,因此具有功能,这是一个示例:

function myClass(){
    this._attr = "foo";
    this.getAttr = function(){
        return this._attr;
    }
}

有没有办法将此对象连同函数一起存储在 redis 中?我尝试了JSON.stringify(),但只保留了属性。如何存储函数定义并能够执行以下操作:

var myObj = new myClass();
var stringObj = JSON.stringify(myObj);
// store in redis and retreive as stringObj again
var parsedObj = JSON.parse(stringObj);

console.log(myObj.getAttr()); //prints foo
console.log(parsedObj.getAttr()); // prints "Object has no method 'getAttr'"

拨打parsedObj.getAttr()时如何获得foo

提前谢谢你!

编辑

有人建议修改 MyClass.prototype 并存储值,但是像这样的东西呢(除了 setter/getter 之外的函数):

function myClass(){
    this._attr = "foo";
    this._accessCounts = 0;
    this.getAttr = function(){
        this._accessCounts++;
        return this._attr;
    }
    this.getCount = function(){
        return this._accessCounts;
    }
}

我试图说明一个函数,该函数在调用时计算诸如计数或平均值之类的东西,除了做其他事情。

【问题讨论】:

标签: javascript json node.js


【解决方案1】:

首先,您没有定义一个类。

它只是一个对象,具有一个值为函数的属性(其在构造函​​数中定义的所有成员函数将在创建新的实例复制,这就是为什么我说它不是一个类。)

使用JSON.stringify 时将被剥离。

假设您使用的是使用 V8 的 node.js,最好的方法是定义一个真实的类,并与__proto__ 玩一点魔法。无论您在类中使用了多少属性(只要每个属性都使用原始数据类型),哪个都可以正常工作。

这是一个例子:

function MyClass(){
  this._attr = "foo";
}
MyClass.prototype = {
  getAttr: function(){
    return this._attr;
  }
};
var myClass = new MyClass();
var json = JSON.stringify(myClass);

var newMyClass = JSON.parse(json);
newMyClass.__proto__ = MyClass.prototype;

console.log(newMyClass instanceof MyClass, newMyClass.getAttr());

将输出:

true "foo"

【讨论】:

  • @scanales 如果它是一个普通对象,它将正常工作。但如果它是类的实例,则需要递归设置其__proto__
【解决方案2】:

不,JSON 不存储函数(这也会非常低效)。相反,使用序列化方法和反序列化构造函数。示例:

function MyClass(){
    this._attr = "foo";
    this.getAttr = function(){
        return this._attr;
    }
}
MyClass.prototype.toJSON() {
    return {attr: this.getAttr()}; // everything that needs to get stored
};
MyClass.fromJSON = function(obj) {
    if (typeof obj == "string") obj = JSON.parse(obj);
    var instance = new MyClass;
    instance._attr = obj.attr;
    return instance;
};

【讨论】:

  • 但是那些不是 setter/getter 的函数呢,我把它们添加到我的问题中
  • @scanales: getCount 也是一个 getter 方法,不是吗?
  • 我的意思是 getAttr 上 accessCounts 的增量,它不仅像以前一样获得静态属性。
  • 由于accessCounts也只是对象的[public]属性,所以与attr没有区别。
  • @bvdb JSON.stringify 将对它访问的所有对象调用toJSON()
【解决方案3】:

Scanales,我遇到了同样的问题,并尝试了一种类似于 Bergi 建议的创建新的序列化/反序列化方法的技术......但发现它对我不起作用,因为我有对象嵌套在对象中(几个深)。如果这是你的情况,那么这就是我解决它的方法。我编写了一个基类(clsPersistableObject),我想要持久化的所有对象都从该基类继承。基类有一个名为 deserialize 的方法,它传递 JSON 字符串。此方法一一设置属性(但不会清除存在的方法),然后递归地推迟到子对象执行相同操作(根据需要多次)。

deserialize: function (vstrString) {
                //.parse: convert JSON string to object state


                //Use JSON to quickly parse into temp object (does a deep restore of all properties)
                var tmpObject = JSON.parse(vstrString);

                //objZoo2.animal.move();

                //Note: can't just do something like this: 
                //  CopyProperties(tmpObject, this);
                //because it will blindly replace the deep objects 
                //completely...inadvertently wiping out methods on it.  Instead:
                //1) set the properties manually/one-by-one.
                //2) on objects, defer to the deserialize on the child object (if it inherits clsPersistableObject)
                //2b) if it doesn't inherit it, it's an intrinsic type, etc...just do a JSON parse.

                //loop through all properties
                var objProperty;
                for (objProperty in tmpObject) {

                    //get property name and value
                    var strPropertyName = objProperty;
                    var strPropertyValue = tmpObject[objProperty]; //note: doing this .toString() will cause

                    if (objProperty !== undefined) {
                        //check type of property
                        if (typeof strPropertyValue == "object") {
                            //object property: call it recursively (and return that value)

                            var strPropertyValue_AsString = JSON.stringify(strPropertyValue);

                            //see if has a deserialize (i.e. inherited from clsPeristableObject)
                            if ("deserialize" in this[objProperty]) {
                                //yes: call it
                                this[objProperty]["deserialize"](strPropertyValue_AsString);
                            }
                            else {
                                //no: call normal JSON to deserialize this object and all below it
                                this[objProperty] = JSON.parse(strPropertyValue_AsString);
                            }  //end else on if ("deserialize" in this[objProperty]) 
                        }
                        else {
                            //normal property: set it on "this"
                            this[objProperty] = tmpObject[objProperty];
                        } //end else on if (typeof strPropertyValue == "object") 
                    } //end if (objProperty !== undefined) 
                }

            }

【讨论】:

    【解决方案4】:

    看起来你试图对一个封闭的函数进行字符串化。您可以使用 ()=>{} 来解决范围问题。

    function myClass(){
        this._attr = "foo";
        this._accessCounts = 0;
        this.getAttr = ()=>{
            this._accessCounts++;
            return this._attr;
        }
        this.getCount = ()=>{
            return this._accessCounts;
        }
    }
    

    【讨论】:

      【解决方案5】:

      你得到的 grom JSON.stringify() 是一个字符串。字符串没有方法。 您需要先评估该字符串,然后才能获得原始对象 及其方法。

      var myObj = new myClass();
      var stringObj = JSON.stringify(myObj);
      

      ---- 编辑 -----

      //Sorry use this:
      var getBackObj  = JSON.parse(stringObj);
      //Not this
      var getBackObj = eval(stringObj);
      
      console.log(getBackObj.getAttr()); // this should work now
      

      【讨论】:

      • JSON.stringify 只是跳过函数成员,所以这不起作用。
      • 我在 [stackoverflow.com/questions/3685703/… 帖子) 上找到了这个函数function toJSONWithFuncs(obj) { Object.prototype.toJSON = function() { var sobj = {}, i; for (i in this) if (this.hasOwnProperty(i)) sobj[i] = typeof this[i] == 'function' ? this[i].toString() : this[i]; return sobj; }; var str = JSON.stringify(obj); delete Object.prototype.toJSON; return str; },它对函数进行了字符串化,但无法获取它们
      • 无法以可用的方式恢复它们,有什么想法吗?
      • eval = 邪恶。尤其不要相信您从带有 eval 的服务器返回的信息。话虽如此, JSON.parse / stringify 不包括不可枚举的属性(如原型)。没有尝试过@scanales 功能,但它似乎合法,除了删除 Object.prototype.toJSON 部分。删除本质上是内存密集型的,这就是为什么 google 的 js 样式指南说不要这样做,而是将属性设置为 null
      猜你喜欢
      • 1970-01-01
      • 2021-06-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-28
      • 2019-09-23
      • 2013-12-11
      • 2018-05-16
      相关资源
      最近更新 更多