JS保持JSON中读取顺序一致的方法
昨天临时写个小脚本,表单内容保存到JSON中,需要按照写入顺序读取。
JS无法直接做到写入和读取顺序一致。本人能力有限,只能想到3种方法,每种方法适用场景不同,要根据前后端需求选择。
一、JSON对象保存成JSON数组。
把键值对拆分开,每个键值对单独保存到每个数组项上,利用数组保证顺序的一致性。
{
"name": "米米奇",
"age": 24,
"city": "仓群峡谷"
}
保存成如下格式:
[
{"name": "米米奇"},
{"age": 24},
{"city": "仓群峡谷"}
]
但这个方法遍历起来很麻烦,因为js遍历时JSON对象时,必须要对key循环。另外查找也很麻烦,不能直接用in
操作符,需要遍历数组,再逐项对比key才可以。
arr.forEach(obj=>{
for(let key in obj){
if(key=="name"){
……
}
}
});
优点:
- 可以保证插入顺序。
- 反序列化和序列化后顺序不变。
- 反序列化后文件体积增加不大。
缺点:
- 遍历查找困难。
二、对JSON数组进行改进。
为了解决第一个方法中遍历和查找困难问题,修改一下数据结构:
[
{key: "name", value: "米米奇"},
{key: "age": value: 24},
{key: "city": value: "仓群峡谷"}
]
明确定义键值对的名称,这样每个数组项遍历和查找时,就可以用key直接定位,不需要嵌套循环了。
arr.forEach(obj=>{
if(obj.key=="name"){
……
}
});
代码相对与第一个确实简单多了。
优点:
- 可以保证插入顺序。
- 反序列化和序列化后顺序不变。
- 遍历查找相对简单。
缺点:
- 反序列化后文件体积变大,尤其值主要为数字时。
- 需要约定好key-value项的名称。
三、其他方法。
- 用额外数组记录key的顺序,暴露给外部自定义的方法(getter、setter、forEach、delete等),但是这样代码要改动好多,原生方法都不能用了,还要维护调用接口,数据一致性难以确保。不推荐自己造轮子。
如果无反序列化要求,可以利用ES6 Proxy代理,记录JSON对象的插入、修改操作,内部数组记录key的顺序,遍历时直接返回内部保存的key数组。直接上代码:
/** * 用于代理实现json遍历顺序和插入时一致 * 用法:var proxy = JSONProxy(); * 然后可以直接用原生方法操作,for(key in obj){}遍历时key就是添加顺序 * 注意:重新赋值时,会把key移到末尾,如果不需要可以修改代码 */ function JSONProxy(obj){ return new Proxy(obj||{}, { // 用于保存健的插入顺序 keys:[], //设置属性时,检测并保存key,这里其实有两种算法,看需求 set: function(target, key, value, receiver){ let index=this.keys.indexOf(key); if(index>=0){ this.keys.splice(index,1); // 删除已有健 } this.keys[this.keys.length]=key; // 添加到末尾 return target[key]=value; }, // 拦截属性删除操作 deleteProperty:function(target, key) { let index=this.keys.indexOf(key); if(index>=0){ this.keys.splice(index,1); } return delete target[key]; }, // 遍历获取键值时的操作,for...in循环,返回一个数组。 ownKeys:function(target){ return this.keys; } }); }
因为使用了代理,修改一下变量定义外,现有代码再不需要改动。代码:
let wuta={}; let person=JSONProxy(wuta); // 赋值 person.name="小虾米" person.country="萌国"; person.city="葫芦岛"; // 循环遍历 for(let key in person){ …… // key顺序'name', 'country','city' } // 获取所有key数组。 let keys=Object.keys(person); // 输出 ['name', 'country', 'city'] // 或者 let keys=Object.getOwnPropertyNames(person); // 上面无法获取内部属性了,JSON也不需要吧。可以从被代理对象获取,但无法排序。
优点:
- 可以保证插入顺序。
- 与原生对象一样的操作方法,现有代码无需改动。
缺点:
- 不能确保反序列化和序列化后顺序。
- 代理对性能会有影响。
结论:
1、持久化或者反序列化有要求时,可以优先采用方法二;
2、只是临时操作,可以考虑Proxy代理模式,操作最方便。