如何对含函数与循环引用的对象进行JSON序列化与反序列化?(JavaScript)
处理带函数与循环引用的对象序列化/反序列化问题
首先明确两个核心限制:
- JSON标准不支持函数类型,默认序列化时函数会被直接忽略
- JSON原生无法处理循环引用,这也是你选择flatted的原因,但flatted本身只解决循环引用,不会处理函数的序列化与还原
下面给两种可行的解决方案:
方案一:手动扩展flatted,处理函数序列化与还原
flatted可以处理循环引用,但需要我们自己加逻辑把函数转成可序列化的字符串,反序列化时再转回去。
代码示例
// 带循环引用和函数的目标对象 const gamePlayer = { name: "Warrior", hp: 150, slash: function() { console.log(`${this.name} uses Slash!`); } }; // 添加循环引用 gamePlayer.currentTarget = gamePlayer; // 自定义序列化函数:标记函数并转成字符串 function saveData(obj) { return flatted.stringify(obj, (key, value) => { if (typeof value === 'function') { // 用特殊前缀标记这是函数字符串 return `[FUNC]${value.toString()}`; } return value; }); } // 自定义反序列化函数:还原函数 function loadData(str) { const parsedObj = flatted.parse(str); // 递归遍历对象,把标记的函数字符串转成可执行函数 function restoreFuncs(target) { if (typeof target !== 'object' || target === null) return target; for (const prop in target) { const val = target[prop]; if (typeof val === 'string' && val.startsWith('[FUNC]')) { // 提取函数体,转成函数 const funcBody = val.slice(6); // 注意:new Function创建的函数需要手动绑定this target[prop] = new Function(`return ${funcBody}`)(); } else if (typeof val === 'object') { restoreFuncs(val); } } return target; } return restoreFuncs(parsedObj); } // 测试保存与加载 const savedStr = saveData(gamePlayer); const loadedPlayer = loadData(savedStr); // 验证循环引用是否保留 console.log(loadedPlayer.currentTarget === loadedPlayer); // 输出 true // 验证函数可用(需要绑定this,因为函数是对象方法) loadedPlayer.slash.call(loadedPlayer); // 输出 "Warrior uses Slash!"
注意事项
- 用
new Function或eval还原函数存在代码注入风险,只适合本地保存的可信数据(比如单机游戏存档),绝对不能用于处理用户上传的不可信数据。 - 对象方法的
this需要手动绑定,因为还原后的函数默认不会绑定到原对象上。
方案二:使用支持函数+循环引用的第三方库
如果不想手动处理,可以用serialize-javascript这类专门的库,它原生支持函数和循环引用的序列化:
const serialize = require('serialize-javascript'); // 序列化:开启unsafe选项以支持函数 const savedStr = serialize(gamePlayer, { unsafe: true }); // 反序列化:注意这里用eval,同样有安全风险 const loadedPlayer = eval(`(${savedStr})`); // 验证功能 console.log(loadedPlayer.currentTarget === loadedPlayer); // true loadedPlayer.slash(); // 输出 "Warrior uses Slash!"
注意事项
- 同样要警惕安全问题,不可用于处理不可信数据。
- 该库会把函数直接序列化为可执行代码,反序列化时直接运行,所以必须确保数据来源安全。
内容的提问来源于stack exchange,提问作者Richard Yang
相关产品推荐
相关产品推荐

