字符串化javascript函数

字符串化javascript函数,javascript,json,Javascript,Json,我正处于游戏开发的最后阶段,我有一堆像这样的东西 roomBedroom = function () { this.title = "Bedroom"; this.description = "I'm in a bedroom"; this.noun = "bed"; this.entities = new Array(); } var bedroom = new roomBedroom(); 我现在想做的是将我所有的游戏对象放入一个数组中 var saved

我正处于游戏开发的最后阶段,我有一堆像这样的东西

roomBedroom = function () {
    this.title = "Bedroom";
    this.description = "I'm in a bedroom";
    this.noun = "bed";
    this.entities = new Array();
}

var bedroom = new roomBedroom();
我现在想做的是将我所有的游戏对象放入一个数组中

var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);
计划是保存savedGameObjects数组,然后在用户再次加载游戏时调用它

如果我替换
savedGameObjects['dombery']=dombery带有
savedGameObjects['bed']='slappy'它工作,但当我有对象时不工作


我真的需要保存对象的当前状态。我不想逐一查看每个对象,而是保存关键信息。

您可以声明一个大变量,如

var world = {};
每个小变量声明为

var bedroom = world.bed = (world.bed || new roomBedroom());

记住,千万不要把卧室换成另一个物体,我想这会很好,但是看起来太冗长了。这感觉有点像黑客,但这是我现在能想到的最好的了

您的序列化/反序列化实用程序

这将在序列化之前将
obj.constructor.name
附加到
obj.\uu prototype
。反序列化后,原型将放回原位

(function(global) {

  function serialize(obj) {
    obj.__prototype = obj.constructor.name;
    return JSON.stringify(obj);
  };

  function deserialize(json) {
    var obj = JSON.parse(json);
    obj.__proto__ = global[obj.__prototype].prototype;
    return obj;
  }

  global.serialize = serialize;
  global.deserialize = deserialize;

})(window);
示例“类”

让我们试试看

var foo = new Foo();

var json = serialize(foo);
console.log(json);

var newFoo = deserialize(json);
console.log('a', newFoo.a); // a
console.log('b', newFoo.b); // b

newFoo.hello(); // hello

注意一些陷阱

如果使用表达式来定义“类”,则将有一个无名构造函数

var Foo = function() {};
var foo = new Foo();
foo.constructor.name; // ""
与命名函数相反

function Foo() {}
var foo = new Foo();
foo.constructor.name; // Foo
为了使
序列化
反序列化
工作,您需要使用命名函数


又一个问题

反序列化
方法期望您的“类”存在于同一命名空间中的上(
窗口,在本例中为
)。您可以用另一种方式封装游戏对象类,只需确保重新配置反序列化方法,以便它可以根据需要找到原型


让它变得更好

您不必将
序列化
附加到全局
窗口
,而可以让
序列化
游戏对象上进行直播。原型
,然后您的单个类可以从
游戏对象
继承。序列化对象将非常简单

var json = foo.serialize();
// {"a":"a","b":"b","__prototype":"Foo"}
然后,您可以将
反序列化
定义为
游戏对象。反序列化
和还原
foo
将是

var foo = GameObject.deserialize(json);

替代解决方案

您可以非常巧妙地使用

这可能有点冗长,但它确实让您可以单独控制如何反序列化/还原游戏对象

var savedData = // your normal JSON here

var player = Player.create(savedData.player);

var items = [];
for (var i=0, i<savedData.items.length; i++) {
  items.push(Item.create(savedData.items[i]));
}

var map = Map.create(savedData.map);
var savedData=//这里是您的普通JSON
var player=player.create(savedData.player);
var项目=[];

对于(var i=0,i如果我在浏览器中运行以下代码,那么获取卧室对象的JSON字符串没有问题,但不确定问题出在哪里

请注意,JSON是数据,而卧房是对象,卧房可能有JSON没有的行为,如
turnflight()

roomBedroom = function () {
    this.title = "Bedroom";
    this.description = "I'm in a bedroom";
    this.noun = "bed";
    this.entities = new Array();
}

var bedroom = new roomBedroom();

var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
//logs {"bedroom":{"title":"Bedroom","description":
//  "I'm in abedroom","noun":"bed","entities":[]}}
console.log(JSON.stringify(savedGameObjects));
因此,如果要从JSON数据重新创建对象实例,则可以更改构造函数:

roomBedroom = function (args) {
    //following fails fast and loud, you could silently
    //fail by setting args to {}
    if(typeof args!=="object")
      throw new Error("Have to create roomBedroom by passing an object");
      //or do args={} to silently fail
    this.title = args.title||"Bedroom";
    this.description = args.description||"I'm in a bedroom";
    this.noun = args.noun||"bed";
    //if entities are objects with behavior
    //  you have to re create them here passing the JSON data
    //  as I've done with roomBedroom
    this.entities = args.entities||new Array();
}
var jsonString='{"bedroom":{"title":"Bedroom",'+
  '"description":"I\'m in a bedroom",'+
  '"noun":"bed","entities":[]}}';
var bedroom = new roomBedroom({});
bedroom.entities.push({hi:"there"});
bedroom.title="Master Bedroom";
//serialize bedroom to a json string
var jsonString = JSON.stringify(bedroom);
//create a roomBedroom instance named br2 using
// the serialized string
var br2=new roomBedroom(JSON.parse(jsonString));

//compare if they are the same
console.log(JSON.stringify(bedroom)===JSON.stringify(br2));//true

我有一个方法可能适合你。你可以

要点是在解析对象时使用to
JSON.parse
来重建对象

我使用一个可配置为多种不同类型的通用恢复器来实现这一点,尽管这里只使用Room卧房构造函数。此实现假定您有简单的复制构造函数,可以使用对现有对象的引用来创建新对象。(关于其他更复杂的可能性,请参见我在2月份给出的)为了简化复制构造函数的使用,我还有一个函数,它接受一个非常简单的构造函数和一组默认值,并为您构建一个复制构造函数

var MultiReviver = function(types) {
    return function(key, value) {
        var type;
        for (var i = 0; i < types.length; i++) {
            type = types[i];
            if (type.test(value)) {
                return new type.constructor(value);
            }
        }
        return value;
    };
};


var makeCloningConstructor = (function() {
    var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
    var F = function() {};

    return function(Constructor, defaults) {
        var fn = function(obj) {
            Constructor.call(this);
            var self = this;
            var config = obj || {};
            Object.keys(defaults).forEach(function(key) {
                self[key] = clone(defaults[key]);
            });
            Object.keys(config).forEach(function(key) {
                self[key] = clone(config[key]);
            });
        };
        F.prototype = Constructor.prototype;
        fn.prototype = new F();
        fn.constructor = Constructor;

        return fn;
    };
})();

// Note: capitalize constructor functions
var RoomBedroom = makeCloningConstructor(function RoomBedroom() {}, {
    title: "Bedroom",
    description: "I'm in a bedroom",
    noun: "bed",
    entities: []  // Note: use `[]` instead of `new Array()`.
});

RoomBedroom.prototype.toggleLight = function() {
    this.lightOn = !this.lightOn;
};

RoomBedroom.prototype.checkLights = function() {
    return "light is " + (this.lightOn ? "on" : "off");
};

var bedroom = new RoomBedroom();
bedroom.windowCount = 3; // add new property
bedroom.noun = "king-sized bed"; // adjust property
bedroom.toggleLight(); // create new propery, use prototype function
console.log(bedroom.checkLights());

var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);

var reviver = new MultiReviver([{
    constructor: RoomBedroom,
    test: function(obj) {
        var toString = Object.prototype.toString, str = "[object String]", 
            arr = "[object Array]";
        return toString.call(obj.title) == str && 
               toString.call(obj.description) == str && 
               toString.call(obj.noun) == str && 
               toString.call(obj.entities) == arr;
    } 
}]);

var retrievedGameObjects = JSON.parse(jsonGame, reviver);

// data comes back intact
console.log(JSON.stringify(retrievedGameObjects, null, 4));
// constructor is as expected
console.log("Constructor: " + retrievedGameObjects.bedroom.constructor.name);
// prototype functions work
console.log(retrievedGameObjects.bedroom.checkLights());
var MultiReviver=函数(类型){
返回函数(键、值){
var类型;
对于(变量i=0;ivar MultiReviver = function(types) {
    return function(key, value) {
        var type;
        for (var i = 0; i < types.length; i++) {
            type = types[i];
            if (type.test(value)) {
                return new type.constructor(value);
            }
        }
        return value;
    };
};


var makeCloningConstructor = (function() {
    var clone = function(obj) {return JSON.parse(JSON.stringify(obj));};
    var F = function() {};

    return function(Constructor, defaults) {
        var fn = function(obj) {
            Constructor.call(this);
            var self = this;
            var config = obj || {};
            Object.keys(defaults).forEach(function(key) {
                self[key] = clone(defaults[key]);
            });
            Object.keys(config).forEach(function(key) {
                self[key] = clone(config[key]);
            });
        };
        F.prototype = Constructor.prototype;
        fn.prototype = new F();
        fn.constructor = Constructor;

        return fn;
    };
})();

// Note: capitalize constructor functions
var RoomBedroom = makeCloningConstructor(function RoomBedroom() {}, {
    title: "Bedroom",
    description: "I'm in a bedroom",
    noun: "bed",
    entities: []  // Note: use `[]` instead of `new Array()`.
});

RoomBedroom.prototype.toggleLight = function() {
    this.lightOn = !this.lightOn;
};

RoomBedroom.prototype.checkLights = function() {
    return "light is " + (this.lightOn ? "on" : "off");
};

var bedroom = new RoomBedroom();
bedroom.windowCount = 3; // add new property
bedroom.noun = "king-sized bed"; // adjust property
bedroom.toggleLight(); // create new propery, use prototype function
console.log(bedroom.checkLights());

var savedGameObjects = {};
savedGameObjects['bedroom'] = bedroom;
var jsonGame = JSON.stringify(savedGameObjects);

var reviver = new MultiReviver([{
    constructor: RoomBedroom,
    test: function(obj) {
        var toString = Object.prototype.toString, str = "[object String]", 
            arr = "[object Array]";
        return toString.call(obj.title) == str && 
               toString.call(obj.description) == str && 
               toString.call(obj.noun) == str && 
               toString.call(obj.entities) == arr;
    } 
}]);

var retrievedGameObjects = JSON.parse(jsonGame, reviver);

// data comes back intact
console.log(JSON.stringify(retrievedGameObjects, null, 4));
// constructor is as expected
console.log("Constructor: " + retrievedGameObjects.bedroom.constructor.name);
// prototype functions work
console.log(retrievedGameObjects.bedroom.checkLights());
roomBedroom = function(){
    this.data = {};
    this.data.title = 'Bedroom'
    /// and so on...
}
roomBedroom.prototype.serialize = function(){
    return JSON.stringify( this.data );
};

roomBedroom.prototype.deserialize = function( jstr ){
    this.data = JSON.parse(jstr);
};
var roomBedroom = function ( title ) {
    this.objectName = "roomBedroom";
    this.title = title;
    this.description = "I'm in a bedroom";
    this.noun = "bed";
    this.entities = new Array();
};
var storage = {};

/// add your supported constructors to this list, there are more programmatic
/// ways to get at the constructor but it's better to be explicit.
storage.constructors = {
    'roomBedroom' : roomBedroom
};

/// take an instance and convert to simple object
storage.to = function( obj ){
    if ( obj.toStorage ) {
        return obj.toStorage();
    }
    else {
        var keep = {};
        for ( var i in obj ) {
            if ( obj.hasOwnProperty(i) && !obj[i].call ) {
                keep[i] = obj[i];
            }
        }
        return keep;
    }
}

/// take simple object and convert to an instance of constructor
storage.from = function( obj ){
    var n = obj && obj.objectName, c = storage.constructors[n];
    if ( n && c ) {
        if ( c.fromStorage ) {
            return c.fromStorage( obj );
        }
        else {
            var inst = new c();
            for ( var i in obj ) {
                if ( obj.hasOwnProperty(i) ) {
                    inst[i] = obj[i];
                }
            }
            return inst;
        }
    }
    else {
        throw new Error('`' + n + '` undefined as storage constructor');
    }
}
var savedGameObjects = {};
    savedGameObjects['bedroom'] = storage.to(new roomBedroom("bedroom"));
    savedGameObjects['bedroom2'] = storage.to(new roomBedroom("bedroom2"));

var jsonGame = JSON.stringify(savedGameObjects);
console.log(jsonGame);

savedGameObjects = JSON.parse(jsonGame);
for( var i in savedGameObjects ) {
    savedGameObjects[i] = storage.from(savedGameObjects[i]);
    console.log(savedGameObjects[i]);
}
roomBedroom.prototype.toStorage = function( obj ){
    var ret = {};
    ret.title = obj.title;
    return ret;
};

roomBedroom.fromStorage = function( obj ){
    var inst = new roomBedroom();
    inst.title = obj.title;
    return inst;
};
roomBedroom.fromStorage = function( obj ){
    return new roomBedroom( obj.title );
};
roomBedroom.fromStorage = function( obj ){
    return new roomBedroom( obj ); // <-- the constructor processes the import.
};