2014-05-04 32 views
0

我最近在用JavaScript测试对象序列化。我已经浏览了一些有关Javascript中预定义对象的序列化和反序列化的问题,但我正在寻找更通用的解决方案。这方面的一个例子是:序列化和反序列化JS对象

function anObject(){ 
    var x = 1; 
    this.test = function(){return x;}; 
    this.add = function(a){x+a;}; 
} 
var x = new anObject(); 
x.add(2); 
console.log(x.test()); 
>>> 3 
var y = deserialize(serialize(x)); 
console.log(y.test()); 
>>> 3 

有没有办法序列化此对象和反序列化,使得反序列化对象仍然可以访问局部变量x,而无需使用该对象的原型(如在this解决方案)?

我已经尝试了将函数作为字符串存储并再次评估,但是无法保存对象的状态。

+0

您不能访问封闭的执行上下文/环境,所以我真的不认为这是可能是你正在尝试做的。 –

+1

您可以使用JSON.parse()上的复位参数将字符串化的方法转换为使用eval或Function的实际函数。您还需要定义Function.prototype.toJSON来生成您的reviver函数可以检测到的吸烟枪签名。有了这两个部分,并且没有方法需要外部变量关闭并且发布使用的属性,则可以实现您的目标。 – dandavis

回答

1

如果没有代码自省和代码重写,我认为不是一个好主意,你所要做的是不可能的。但是,这样的事情呢?

function AnObject() { 
    var x = 1; 

    this.x = function() { return x; }; 
    this.addToX = function (num) { x += num; }; 
    this.memento = function() { 
     return { x: x }; 
    }; 
    this.restoreState = function (memento) { 
     x = memento.x; 
    }; 
} 


var o = new AnObject(); 

o.addToX(2); 
o.x(); //3 

var serializedState = JSON.stringify(o.memento()), 
    o = new AnObject(); 

o.restoreState(JSON.parse(serializedState)); 

o.x(); //3 

然而,请注意,有priviledged成员付出了巨大的代价因为你失去使用原型的益处。出于这个原因,我宁愿不执行真正的隐私,并依靠命名约定,如this._myPrivateVariable来代替(除非你隐藏了模块的成员)。

0

感谢您的回复。虽然plalx的答案完美适用于特定对象,但我想要有更通用的东西,它适用于任何对象。

另一种解决方案可以使用是这样的:

function construct(constructor, args, vars) { 
    function Obj() { 
     var variables = vars 
     return constructor.apply(this, args); 
    } 
    Obj.prototype = constructor.prototype; 
    return new Obj(); 
} 

function addFunction(anObject, aFunction, variables) { 
    var objectSource = anObject.toString(); 
    var functionSource = aFunction.toString(); 
    objectSource = objectSource.substring(0,objectSource.length-1); 
    var functionName = functionSource.substring(9, functionSource.indexOf('(')); 
    var functionArgs = functionSource.substring(functionSource.indexOf('('), functionSource.indexOf('{')+1); 
    var functionBody = functionSource.substring(functionSource.indexOf('{')+1, functionSource.length); 
    return objectSource + "this." + functionName + " = function" + 
      functionArgs + "var variables = " + variables + ";\n" + functionBody + "}"; 
} 

function makeSerializable(anObject) { 
    var obj = JSON.stringify(anObject, function(key, val) { 
      return ((typeof val === "function") ? val+'' : val); 
     }); 
    var variables = []; 
    while(obj.indexOf("var") > -1) { 
     var subString = obj.substring(obj.indexOf("var")+3, obj.length-1); 
     while (subString[0] == " ") 
      subString = subString.replace(" ", ""); 
     var varEnd = Math.min(subString.indexOf(" "), subString.indexOf(";")); 
     var varName = subString.substring(0, varEnd); 
     variables.push(varName); 

     obj = obj.replace("var",""); 
    } 

    var anObjectSource = addFunction(anObject, 
     function serialize(){ 
      var vars = []; 
     console.log("hidden variables:" + variables); 
     variables.forEach(function(variable) { 
      console.log(variable + ": " + eval(variable)); 
      vars += JSON.stringify([variable, eval(variable)]); 
     }); 

     var serialized = []; 
     serialized.push(vars); 
     for (var func in this){ 
      if (func != "serialize") 
       serialized.push([func, this[func].toString()]); 
     } 
     return JSON.stringify(serialized); 
     }, 
     JSON.stringify(variables)); 
    anObject = Function("return " + anObjectSource)(); 

    var params = Array.prototype.slice.call(arguments); 
    params.shift(); 

    return construct(anObject, params, variables); 
} 

这可以让你任何对象序列化的所有要素,包括隐藏变量。然后,serialize()函数可以替换为隐藏变量的自定义字符串表示形式,可以在将字符串表示形式反序列化为对象时使用它。

用法:

function anObject(){ 
    var x = 1; 
    var y = [1,2]; 
    var z = {"name": "test"}; 
    this.test = function(){return x;}; 
    this.add = function(a){x+a;}; 
} 

var test = makeSerializable(anObject) 

test.serialize() 
>>>["[\"x\",1][\"y\",[1,2]][\"z\",{\"name\":\"test\"}]",["test","function(){return x;}"],["add","function (a){x+a;}"]] 
+0

它的工作原理,但它有点hacky和顺便说一句,得到一个函数的来源不需要使用'JSON.stringify',你可以做'anObject.toString()'。既然你修改了构造函数,它也会破坏一些东西,比如'instanceof anObject'返回false。您应该避免强制实施真正的隐私,这将允许您使用原型,并避免必须执行代码自省和重写来序列化您的对象。 – plalx